美文网首页
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