美文网首页
Java 并发锁

Java 并发锁

作者: Tinyspot | 来源:发表于2023-04-05 21:46 被阅读0次

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

相关文章

网友评论

      本文标题:Java 并发锁

      本文链接:https://www.haomeiwen.com/subject/aqfyddtx.html