文章目录
锁:用于控制多个线程对共享资源访问的机制,确保任何给定时间只有一个线程能访问共享资源,防止多线程并发访问引起的数据竞争和不一致问题。
锁分类
按照框架技术分类:
- java中的锁
- sychronized关键字
- Lock接口
- mysql中的锁
- 表锁
- 行锁
- 读写锁
- 自增锁
- 分布式锁
按照公平性/非公平性分类:
- 公平锁:按照线程请求锁的顺序来授予锁,确保先请求的线程优先获取锁。
- 非公平锁:不保证请求顺序,可能导致某些线程长期得不到锁。通常性能更好,但可能引入"饥饿"现象。
按照乐观/悲观分类:
- 乐观锁:认为自己这个线程在使用数据的时候不会有其他线程来修改数据,在更新数据前先判断有没有其他线程修改了该数据。如果没有,将更新的数据写入;如果有,就会根据不同的情况执行不同的操作(例如,报错)
- 悲观锁:认为自己这个线程在使用数据的时候会有其他线程来修改数据,所以在获取数据之后会加锁,防止被别的线程修改。
悲观锁适合写 操作多 的场景(确保写操作数据正确),乐观锁适合读操作多的场景(可以使读操作性能提升)。
其他:
- ThreadLocal:Java中的一种类,虽然不属于锁的范畴,但也能解决线程安全问题。在多线程访问过程中,如果把数据放在ThreadLocal类中,不同线程得到的是该变量数据的副本,可以避免线程安全问题。
锁介绍
- sychronized:
- 作用域:Java中的关键字,可作用于代码块、方法。
- 公平性:非公平锁,不管多线程的顺序性,所有线程都可以尝试获取锁资源。
- 由于在sychronized中锁的获取、释放是++隐式的++ ,为了方便锁的管理,避免不必要的资源损耗,在synchronized中存在一个锁升级 的过程(无锁->偏向锁->轻量级锁->重量级锁,该过程不可逆,只能升级不能降级),具体来说:
- 无锁:就是没有锁,所有线程都可以对资源进行访问,不存在锁竞争关系。
- 偏向锁:也就是偏向于第一个访问锁资源的线程,第一个访问锁资源的线程会得到偏向锁,锁对象中会保存线程的ID。一旦有第二个线程来访问锁资源,偏向锁将撤销线程ID信息,升级为轻量级锁。(适用于单线程)
- 轻量级锁:线程以CAS(Compare And Switch)无锁机制来尝试获取锁,如果已有线程占有锁,线程将进入自旋状态(占有CPU资源,等待再次获取锁),jvm中会设置自旋等待的最大次数,如果有线程达到最大自旋次数却还是没能获取到锁资源,轻量级锁将会升级为重量级锁。轻量级锁一般适用于加在执行时间较短的代码上,因为自旋虽然能减少CPU上下文切换的开销,但同时也会占用CPU资源,处于忙等的状态。
- 重量级锁:没有获得锁资源的线程将阻塞,等待再次唤醒。
- Lock接口:相对于synchronized,实现了更灵活的锁机制。
-
可显式的获取/释放锁;
java.lock();//获取锁 .unlock();//释放锁
-
可实现更复杂的锁策略:
- 可中断锁:
tryLock(long timeout, TimeUnit unit)
- Condition对象:与
Lock
结合使用,允许线程在特定条件下等待和唤醒,适用于复杂的线程协作。
- 可中断锁:
-
含多个锁的实现类:
-
ReentrantLock:可重入锁(同一线程可多次获取同一把锁)
-
公平性:可选公平/不公平,构造方法中可设置公平性
java//构造函数 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } //使用 private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
-
-
ReentrantReadWriteLock:读写锁
- 公平性:可选(与ReentrantLock类似)
- 允许多个线程同时读数据,但写操作会独占锁。
-
StampedLock:另一种高效的读写锁。
- 在读操作频繁的情况下,StampedLock的性能优于ReentrantReadWriteLock。
- StampedLock允许在乐观读和悲观读之间进行选择,提供了更大的灵活性。在某些情况下,如果乐观读失败,可以选择获取悲观读锁。而ReentrantReadWriteLock本身不实现乐观锁的机制。
-
-
- mysql中的锁:
- 表锁:MyISAM存储引擎仅能实现表锁,当修改某一行数据时,整张表都会被锁起来。
- 行锁:依赖于InnoDB存储引擎,使用 MVCC 来实现行级锁(依赖于在每一行数据中维护一个隐藏的版本号)。
- 读写锁:MySQL 存在读写锁的概念,允许多个线程同时读取数据,但在写入时会独占锁。
- 自增锁:在插入自增字段时,MySQL 会加自增锁以保证自增值的唯一性。
- 分布式锁:
- 适用于分布式环境下,对于多个jvm来管理的锁资源,能够实现分布式锁的技术包括但不限于:redis、zookeeper等
锁选择
- synchronized:
- 适用于较为简单的同步场景
- 考虑代码简洁性(不需要显式的获取、释放锁)
- 无需公平性
- Lock:
- 较复杂的锁策略
- 高竞争环境
- 需要公平性
- 分布式锁:
- 分布式环境下,多个jvm适用
以上为个人学习分享,如有问题,欢迎指出:)