栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java并发学习笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java并发学习笔记

一、JAVA如何开启线程?怎么保证线程安全?

线程跟进程的区别:进程是操作系统进行资源分配的最小单元。线程是操作系统进行任务分配的最小单元,进程隶属于线程

如何开启线程?

1.继承Thread,重写run方法;2.实现Runnable接口,实现run方法(常用)

3.实现Callable接口,实现call方法。通过FutureTask创建一个线程,获取到线程执行的返回值;4.通过线程池来开启线程。

怎么保证线程安全?核心:加锁

如何加锁:1.使用JVM提供的锁,也就是Synchronized关键字;2. JDK提供的各种锁lock

二.Volatile和Synchronized有什么区别?Volatile能不能保证线程安全?DCL(Double Check Lock)单例为什么要加Volatile?
  1. Synchronized关键字用来加锁,Volatile只是保持变量的线程可见性。通常适用于一个线程写多个线程读的场景

  2. 不能,Volatile关键字只能保证线程可见性,不能保证原子性

    public class VolatileDemo2 {
    public static  boolean flag = true;
    public static void main(String[] args) {
    	new Thread(()->{
    		while(flag){
    		}
    		System.out.println("========End of Thread1=========");
        }).start();
    	try {
    		Thread.sleep( millis: 100);
    	}catch (InterruptedException e) {
    		e.printStackTrace();
    	}
    	System.out.println("turn flag off");
        flag=false;
    	}
    }
    
    

    Volatile能使线程的改变立马传递给其他线程,保证可见性

    3.作用 Volatile防止指令重排。在DCL中,防止高并发情况下指令重排造成的线程安全问题

    指令重排:指在cup中 先分配内存 再初始化 再建立指针关系 多线程中当指针初始化过程中另一个线程访问,则会导致线程安全出现问题

    public class SingleDemo1{
    //	private static SingleDemo1 singleDemo1=new SingleDemo1();
        private static SingleDemo1 singleDemo1;
    	private SingleDemo1(){}
        
        public static   SingleDemo1 getInstance(){
            if(null == singleDemo1){
                synchronized(SingleDemo1.class){
                    if(null == singleDemo1){  //DCL单例  Double Check Lock
                    singleDemo1 = new SingleDemo1();
                    }
                }    
            }
            return singleDemo1;
        }
    }
    
三、JAVA线程锁机制是怎样的?偏向锁、轻量级锁、重量级锁有什么区别?锁机制是如何升级的?
  1. Java的锁就是再对象的Markword中记录一个锁状态,偏向锁、轻量级锁、重量级锁、无锁对应不同的锁状态
  2. Java的锁机制就是根据资源竞争的激烈程度不断进行锁升级的过程
四、谈谈你对AQS的理解。AQS如何实现可重入锁?

1、AQS是一个JAVA线程同步的框架。是JDK中很多锁工具的核心实现框架。
2、在AQS中,维护了一个信号量state和一个线程组成的双向链表队列。其中,这个线程队列,就是用来给线程排队的,而state就像是一个红绿灯。用来控制线程排队或者放行的。在不同的场景下,有不用的意义。
3、在可重入锁这个场景下,state就用来表示加锁的次数。0标识无锁,每加一次锁,state就加1。释放锁state就减1。

五、有A,B,C三个线程,如何保证三个线程同时执行?

如何在并发情况下保证三个线程依次执行?如何保证三个线程有序交错进行?

三种线程方式:

LountDownLatch:发令枪 开枪一起跑

CylicBarrier:定义一个座位 来一个线程就-1 满了再开始走

Semaphore:设置固定的权重信号数量,给每个线程分权重信号 到达权重才能走 走完返还权重信号

static volatile int ticket=1;//设置状态
 Thread t1 = new Thread(()->{
while(true){
	if(ticket ==1){
	try {
	Thread.sleep( millis: 100);
		for (int i = 0; i< 10; i++) {
			system.out.println("a"+i);
		}
	ticket=2;
    //对状态+1  下一个进程判断ticket是否=2,=2则可以进行 否则不行
六、如何对一个字符串快速进行排序?

Fork/Join框架 对半拆分到两两比较 然后再根据指针进行汇总

public class mergeTest {
private static int MAX =100;
private static int inits[] = new int[MAX];
//随机队列初始化
static {
	Random r = new Random();
	for (int index = 1; index <= MAX; index++) {
	inits[index - 1] = r.nextInt(1000);
	}
}

public static void main(String[ ] args) throws Exception {
//正式开始
long beginTime =system.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
MyTask task = new MyTask(inits);
ForkJoinTask taskResult = pool.submit(task);
    try {
		int[] ints = taskResult.get();
		system.out.println(Arrays.toString(ints));
} catch (InterruptedException | ExecutionException e) {
	e.printStackTrace(system.out);
}
long endTime = system.currentTimeMillis();
system.out.println("耗时=" +(endTime - beginTime));
}
    
static class MyTask extends RecursiveTask {
private int source[];
public MyTask(int source[]) { this.source = source; }
@Override
protected int[] compute() {
int sourceLen = source.length;
//如果条件成立,说明任务中要进行排序的集合还不够小
    if (sourceLen > 2) {
		int midIndex = sourceLen / 2;
        //拆分成两个子任务
		MyTask task1 = new MyTask(Arrays.copy0f(source, midIndex));
        task1.fork();
		MyTask task2 = new MyTask(Arrays.copy0fRange(source,midIndex,sourceLen));
        task2.fork(O;
//将两个有序的数组,合并成一个有序的数组
         int result1[] = task1.join();
		int result2[] = task2.join();
		int mer[] = joinInts(result1,result2);
//system.out.println( "----——-"+Thread .currentThread ( ) .getName() );
         return mer;
}
//否则说明集合中只有一个或者两个元素,可以进行这两个元素的比较排序了
else {
//如果条件成立,说明数组中只有一个元素,或者是数组中的元素都已经排列好位置了
       if (sourceLen == 1 || source[0] <= source[1]) {
           return source;
		}else {
			int targetp[] = new int[sourceLen];
           	 targetp[0] = source[1];
			targetp[1] = source[0];
           	 return targetp;
		}
	}
}

  

七、并发的三大特性

1.原子性:原子性是指在一个操作中CPU不可以在中途暂停然后再调度,即不被中断操作,要不全部执行完成,要不都不执行。就好比转账,从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。2个操作必须全部完成。

2.可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
若两个线程在不同的CPU,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。

3.虚拟机在进行代码编译时,对于那些改变顺序之后不会对最终结果造成影响的代码,虚拟机不一定会按照我们写的代码的顺序来执行,有可能将他们重排序。实际上,对于有些代码进行重排序之后,虽然对变量的值没有造成影响,但有可能会出现线程安全问题。

八、说说对线程安全的理解

​ 不是线程安全、应该是内存安全,堆是共享内存,可以被所有线程访问
​ 当多个线程访问一个对象时,如果不用进行额外的同步控制或其他的协调操作,调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的
​ 堆是进程和线程共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部+孙走用厂刀配工1问Jo堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是用完了要还给操作系统,要不然就是内存泄漏。
​ 在Java中,堆是Java虚拟机所管理的内存中最大的一块,是所有线程共享的一块内存区域,在虚拟机启动时创建。堆所存在的内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。
​ 栈是每个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是线程安全的。操作系统在切换线程的时候会自动切换栈。栈空间不需要在高级语言里面显式的分配和释放。
​ 目前主流操作系统都是多任务的,即多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。
​ 在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/331626.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号