文章目录
volatile
java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 🈲指令重排
JMM内存模型
- 可见性
- 原子性
- 有序性
CAS
比较并交换compareAndSwap,是一条CPU并发原语,比较当前工作内存与主内存的值,如果相同则执行相关操作,如果不同则继续比较直到主内存与工作内存中的值一致为止。
❓atomicInteger用CAS不用synchronized的原因:
- synchronized对整段操作加锁,在竞争条件下会阻塞等待资源,如果竞争不到资源返回失败,降低并发性,而CAS可以相对减少阻塞时间
- CAS是一条CPU并发原语(用于判断当前工作内存中的值与主内存中值是否一致,如果是则更新,这个过程是原子的,即执行过程连续,不允许被中断
- CAS通过调用unsafe类(可以直接操作内存数据,内部方法都是native修饰),根据内存偏移地址获取数据
缺点:
- 循环时间长,开销大
- 只能保证一个共享变量的原子性
- ABA问题
cas算法实现一个重要前提是,线程在某时刻从主内存中取出数据并在当前时刻进行比较并替换,这个时间差可能会导致数据在中间过程发生过变化。即可能某个线程cas操作成功,但并不代表其中间过程是没有问题的。
⤵️
原子引用:
AtomicReference
⤵️
时间戳原子引用(ABA问题的解决)
AtomicStampedReference
集合类不安全的问题:
- 故障现象: java.util.ConcurrentModificationException
- 导致原因 并发争抢修改导致
- 解决方案
①vector类等线程安全的容器替换
②Collections工具类:
Collections.synchronizedList(new Arraylist<>())
③JUC解决方法:
CopyOnWriteArraylist类:
new CopyOnWriteArraylist<>()
写时复制,应用读写分离的思想
🚩 CopyOnWrite容器即写时复制容器,往一个容器添加元素时,不直接往当前容器的Object[]添加,而是先将当前容器进行copy,在新的容器中添加元素,元素添加完成后,将原容器的引用指向新的容器。
这样做的好处是可以对容器进行并发读,而不需要加锁,读和写使用不同的容器
锁
公平锁和非公平锁
是否按照申请锁的先来后到的顺序获取锁,并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁,非公平锁的优点是吞吐量比公平锁大。对于synchronized而言,也是一种非公平锁。
- 公平锁,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就加入到等待队列中,以后会按照FIFO规则从队列中取到自己;
- 非公平锁,线程到来时尝试占有锁,如果尝试失败,就再采用类似公平锁的方式。
可重入锁(递归锁)
reentrantLock和synchronized就是典型的可重入锁
可重入锁最大的作用是防止死锁
指的是同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,进入内层方法也会自动获取锁。
🚩线程可以进入任何一个它已经拥有的锁所同步着的代码块。
(加锁和解锁次数只要匹配就可以)
自旋锁(spinlock)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
独占锁(写锁)/共享锁(读锁)/互斥锁
- 独占锁:指该锁一次只能被一个线程所持有,sychronized和ReentrantLock是独占锁。
- 共享锁:指该锁可以同时被多个线程所持有。
对ReentantReadWriteLock,其读锁是共享锁,写锁是独占锁。该锁的共享锁可以保证并发读非常高效,读写,写读,写写的过程都是互斥的。 - 🚩多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行,但是,如果有任何一个线程想去写共享资源,就不应该再有其他线程可以对该资源进行读或写。
🚩写操作:原子+独占,整个过程必须是完整的统一体,中间不许被分割,不许被打断
CountDownLatch & CyclicBarrier & Semaphore
CountDownLatch
(秦灭六国,一统天下)
计数器,直到计数为0时await()方法才不被阻塞。
让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒。
CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会被阻塞),当计数器的值变为0时,因调用await方法被阻塞的线程被唤醒,继续执行。
CyclicBarrier
(集齐七颗龙珠就能召唤神龙)
可循环使用的屏障。它要做的事是,让一组线程到达一个屏障(也叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才能继续干活,线程进入屏障通过CyclicBarrier的await方法。
CyclicBarrier与CountDownLatch的区别:CyclicBarrier可重复多次,而CountDownLatch只能是一次.
Semaphore
信号量,可以代替synchronized和lock,信号量设置为1退化成synchronized
信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,一个是用于并发线程数的控制。
正常的锁(concurrency.locks或synchronized锁)在任何时刻都只允许一个任务访问一项资源,而 Semaphore允许n个任务同时访问这个资源。