多线程中锁的总结
乐观锁
认为自己在使用数据的时候不会被别的线程来修改数据,不会加锁,只是在更新数据的时候去判断之前有没有被别的线程更新了这个数据。
CAS(Compare-and-Swap)算法,
AtomicInteger这类原子类就是CAS
悲观锁
认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
synchronized和ReentrantLock
适合写入多的场景,先加锁保证写操作的正确性。
自旋锁
自旋锁基于CAS算法实现的,CPU不会释放。
当线程A获取锁的时候,该锁被其他线程占用,线程A会循环等待,不断尝试获取锁,直到获取到锁才退出循环。
缺点:一直自旋,对CPU的开销比较大
互斥锁
会释放CPU的执行权,进入阻塞状态。
优点:线程在等待锁的过程中不会消耗 CPU 时间片,这有助于提高系统的整体效率
CAS/自旋锁的ABA问题
概念:在并发环境下,线程A读取某个变量是A,进行一次CAS修改前,该变量可能被其他线程修改为B,再改回A,虽然值从A-B-A,但本质上已经发生过变化,而单纯的CAS操作无法感知这个过程,从而可能导致逻辑错误。
解决办法:使用带版本号的原子引用 AtomicStampedReference
synchronized锁升级(JDK之后1.6)
前提:对象存储在JVM的堆当中,每个对象分为4块,第一块(markword)就是存储锁的信息
无锁,在markword标识为二进制001
偏向锁,只有一个线程使用该锁。
- jvm启动4秒之后,创建的对象才会开启偏向锁。在markword标识为二进制101
轻量级锁 两个线程串行的情况下(不存在并发),获取了同一把锁,会有偏向锁升级为轻量级锁,
轻量级锁是CAS,在markword标识为二进制00
如果不想让轻量级锁升级到重量级锁,可以使用分段CAS
重量级锁,两个或两个以上的线程并发获取同一把锁,会升级为重量级锁,在markword标识为二进制10
公平锁和非公平锁
公平锁:先等待的线程可以先获得锁
非公平锁:CPU会优先执行这个线程所有的锁,再切换到其他线程执行其他线程的锁
synchronized是非公平锁,CPU减少了上下文切换,执行效率更高。
ReentrantLock可以指定公平锁和非公平锁。默认是非公平锁,创建公平锁:ReentrantLock reentrantLock = new ReentrantLock(true);
排它锁和共享锁
排它锁:是多个线程同时读和写同一个资源,需要互斥的锁。
共享锁:是多个线程同时读某一个资源,不用等待锁释放,可以共享同一把锁。
synchronized是排它锁。
ReentrantLock是排它锁也是共享锁。
ReentrantReadWriteLock.WriteLock是互斥锁。
ReentrantReadWriteLock.ReadLock是共享锁。

浙公网安备 33010602011771号