目录
前言
锁策略不仅仅局限于JAVA语言中,任何和锁有关的场景都会涉及锁的策略问题。
一、乐观锁&悲观锁
乐观锁:在操作数据之前认为该数据不会被别的线程修改,因此不进行加锁,在修改数据之前,对数据进行判断,如果其他线程对数据进行了修改,则终止操作。(读多写少,对于数据更多的是读,没有冲突)。
乐观锁的一个重要功能就是要检测出数据是否发生访问冲突,可以引入一个“版本号”来解决
悲观锁:在操作数据之前认为数据会被进行修改,所以在每次拿数据的时候都会上锁,别的线程想拿数据就会产生阻塞,直到它拿到锁。(写多读少,对于数据更多的是写,冲突较多)。
乐观锁和悲观锁没有好坏之分。
二、读写锁
-
为什么要有读写锁
线程在对数据进行操作时需要保证线程的安全问题:
两个线程进行只读操作,没有线程安全问题,进行并发读取;
一个线程读一个线程写,会产生线程安全问题;
两个线程同时进行写操作,也会产生线程安全问题;
由此可得当线程进行写操作时都会产生线程安全的问题。
-
读写锁的实现
读写锁就是把读操作和写操作进行区分对待,JAVA标准库中的ReentrantReadWriteLock类可以直接实现读写锁。
读锁(共享锁):
ReetrantReadWriteLock.ReadLock
写锁:
ReentrantReadWriteLock.WriteLock
-
读写锁的互斥性
读加锁与读加锁,不互斥;
读加锁与写加锁,互斥;
写加锁与写加锁,互斥;
三、重量级锁&轻量级锁
重量级锁:加锁机制依赖mutex互斥锁,且大量内核态用户态切换(做的多性能慢)。
轻量级锁:加锁机制尽可能不使用mutex互斥锁,少量内核态用户态切换(做得少性能快)。
重量级锁和轻量级锁是相对的。
用户态代码:代码执行时间是用户可控的。eg.线程池,应用程序
内核态代码:由操作系统来执行,代码具体什么时候执行是不可控的。eg.Thread.start
四、自旋锁&挂起等待锁
-
自旋锁
自旋锁获取锁失败后,会立即再获取锁,无限循环直到获取锁为止,一旦锁被其他线程释放,能够在第一时间获取锁。
自旋锁伪代码:
while(抢锁(lock)==失败){ }
-
挂起等待锁
挂起等待锁在获取锁失败后会进入阻塞状态,等待再次被调度。
五、公平锁&非公平锁
公平锁:遵守先来后到,B比C先来,当A线程运行结束释放锁后,B能先于C获得锁。
非公平锁:不遵守先来后到,B比C先来,当A线程运行结束释放锁后,B、C以及其他线程同时竞争一把锁,谁先抢到谁获得。
六、可重入锁&不可重入锁
可重入锁:允许同一个线程多次获得一把锁,不会将自己锁死
不可重入锁:能够将自己锁死
总结
以上就是今天要讲的内容,本文简单介绍了有关于常见锁的策略。