1. 简介
作用:锁提供对共享资源的独占访问,用来保证线程同步
2. 锁的分类
2.1 乐观锁与悲观锁
悲观锁:在数据被处理前先对数据进行加锁(排它锁)
应用场景:
悲观锁适用于写多读少的场景
乐观锁适用于读多写少的场景(若失败,应该加上重试)
2.2 公平锁与非公平锁
根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁
示例:
synchronized 只支持非公平锁
ReentrantLock 默认非公平锁,可设置公平锁
@Test
public void reentrantLock() {
ReentrantLock fairLock = new ReentrantLock(true);
ReentrantLock nonFairLock = new ReentrantLock();
ReentrantLock nonFairLock2 = new ReentrantLock(false);
}
2.3 独占锁与共享锁
- 根据锁只能被单个线程持有还是能被多个线程共同持有,锁可以分为独占锁和共享锁
- 独占锁是悲观锁
示例:
ReentrantLock 属于独占锁
ReentrantReadWriteLock 属于共享锁
扩展:
互斥锁是独占锁的一种实现
读写锁是共享锁的一种实现。读写锁管理一组锁,一个是只读的锁,一个是写锁
2.4 可重入锁
可重入锁又名递归锁,是指同一个线程在外层方法获取了锁,在进入内层方法会自动获取锁
示例:ReentrantLock 和 synchronized 都是可重入锁
可重入锁分两种:
- Synchronized 隐式锁
- Lock 显示锁
2.5 自旋锁
指线程在没有获得锁时不是被直接挂起,而是自旋(多次尝试获取)
设计自旋锁的原因:
线程的阻塞和唤醒需要CPU从用户态转为核心态
当一个线程在获取锁(比如独占锁)失败后,会被切换到内核状态而被挂起。当该线程获取到锁时又需要将其切换到内核状态而唤醒该线程
2.6 轻量级锁、重量级锁与偏向锁
JDK1.6 前,synchronized 关键字只表示重量级锁
JDK1.6 后,针对 synchronized 做了大量优化,引入 4 种锁状态
锁升级:无锁->偏向锁->轻量级锁->重量级锁
偏向锁
是等到竞争出现才释放锁,即当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,可降低获取锁的代价
轻量级锁
是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能
锁 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
偏向锁 | 无实际竞争,且将来只有第一个申请锁的线程会使用锁 | 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 | |
轻量级锁 | 无实际竞争,多个线程交替使用锁;允许短时间的锁竞争 | 追求响应时间,锁占用时间很短 | |
重量级锁 | 线程竞争不使用自旋,不会消耗CPU | 线程阻塞,响应时间慢(有实际竞争,且锁竞争时间长) | 追求吞吐量,锁占用时间较长 |
3. 锁(Lock)
- Lock 是个接口,使用的是其实现类
- lock() 上锁, unlock() 释放锁
/**
* Lock l = ...;
* l.lock();
* try {
* // access the resource protected by this lock
* } finally {
* l.unlock();
* }
*/
public interface Lock {
void lock();
boolean tryLock();
void unlock();
}
3.1 重入锁(ReentrantLock)
public class ReentrantLock implements Lock, java.io.Serializable {
}
3.2 读写锁(ReentrantReadWriteLock)
- 支持
一写多读,读写分离
,可分别分配读锁、写锁 - 互斥规则
- 写-写:互斥、阻塞
- 读-写:互斥,读阻塞写、写阻塞读
- 读-读:不互斥、不阻塞
- 适用于读多写少的操作
/**
* private final Map<String, Data> m = new TreeMap<String, Data>();
* private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
* private final Lock r = rwl.readLock();
* private final Lock w = rwl.writeLock();
*/
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
}
4. 其他:Java 多线程解决线程安全问题的方式
方式一:Synchronized
- 同步代码块
- 同步方法
方式二:同步锁 Lock
网友评论