多线程八 Lock (二)

简介: 多线程八 Lock (二)

JDK8新增StampedLock对ReentrantReadWriteLock进行增强#


stamped:贴上邮票的; 盖上邮戳的,拿到锁之后会返回给我们一个票据,根据这个Stamp的值判断是在读的时候发生了写,还是在写的时候发生了读操作


解决的问题:


在高并发的情况下,读操作的次数远远大于写操作,,因为读写互斥,写操作可能就会出现饥饿的情况,一直抢占不到cpu的资源

解决方法:

  1. 当然可以使用公平的ReadWriteLock,但是依然有性能问题
  2. StampedLock的乐观锁实现了读写共享提升了!


StampedLock里面有两种锁


乐观锁:


读锁并不会阻塞写锁


public long tryOptimisticRead() {...}


悲观锁:


读写互斥,和ReentrantReadWriteLock实现相同的功能


API:

独占的获取写锁,若锁被其他线程获取到,则阻塞,注意它是相对于ReentrantReadWriteLock来讲,它是有返回值的,返回值的作用:

  • 释放锁(unlock的需要参数)
  • 进行锁的转换


/**
     * Exclusively acquires the lock, blocking if necessary
     * until available.
     *
     * @return a stamp that can be used to unlock or convert mode
     */
    public long writeLock() {
        long s, next;  // bypass acquireWrite in fully unlocked case only
        return ((((s = state) & ABITS) == 0L &&
                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                next : acquireWrite(false, 0L));
    }



//一旦写锁可用立即获取,返回值可以用作释放锁或被锁的转换使用, 0表示没有获取到锁
     /**
     * Exclusively acquires the lock if it is immediately available.
     *
     * @return a stamp that can be used to unlock or convert mode,
     * or zero if the lock is not available
     */
    public long tryWriteLock() {
        long s, next;
        return ((((s = state) & ABITS) == 0L &&
                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                next : 0L);
    }
    //延迟获取锁
         /**
     * Exclusively acquires the lock if it is available within the
     * given time and the current thread has not been interrupted.
     * Behavior under timeout and interruption matches that specified
     * for method {@link Lock#tryLock(long,TimeUnit)}.
     *
     * @param time the maximum time to wait for the lock
     * @param unit the time unit of the {@code time} argument
     * @return a stamp that can be used to unlock or convert mode,
     * or zero if the lock is not available
     * @throws InterruptedException if the current thread is interrupted
     * before acquiring the lock
     */
    public long tryWriteLock(long time, TimeUnit unit)
    ...
    // 非独占的获取读锁
      /**
     * Non-exclusively acquires the lock, blocking if necessary
     * until available.
     *
     * @return a stamp that can be used to unlock or convert mode
     */
    public long readLock() {
        long s = state, next;  // bypass acquireRead on common uncontended case
        return ((whead == wtail && (s & ABITS) < RFULL &&
                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
                next : acquireRead(false, 0L));
    }
    // 一旦锁可用,立即非独占的获取读锁
    /**
     * Non-exclusively acquires the lock if it is immediately available.
     *
     * @return a stamp that can be used to unlock or convert mode,
     * or zero if the lock is not available
     */
    public long tryReadLock() {
        for (;;) {
            long s, m, next;
            if ((m = (s = state) & ABITS) == WBIT)
                return 0L;
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                    return next;
            }
            else if ((next = tryIncReaderOverflow(s)) != 0L)
                return next;
        }
    }


乐观锁!!!它获取到的锁,读写锁非互斥

返回一个标记,这个标记过一会用去 校验, 如果锁是排它锁,返回零


/**
     * Returns a stamp that can later be validated, or zero
     * if exclusively locked.
     *
     * @return a stamp, or zero if exclusively locked
     */
    public long tryOptimisticRead() {
        long s;
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
    }


校验,如果锁还没被任何线程获取,获取被持有当前stamp的线程获取返回true , 如果 stamp为0,返回false


/**
     * Returns true if the lock has not been exclusively acquired
     * since issuance of the given stamp. Always returns false if the
     * stamp is zero. Always returns true if the stamp represents a
     * currently held lock. Invoking this method with a value not
     * obtained from {@link #tryOptimisticRead} or a locking method
     * for this lock has no defined effect or result.
     *
     * @param stamp a stamp
     * @return {@code true} if the lock has not been exclusively acquired
     * since issuance of the given stamp; else false
     */
    public boolean validate(long stamp) {
        U.loadFence();
        return (stamp & SBITS) == (state & SBITS);
    }


锁的释放


/**
     * If the lock state matches the given stamp, releases the
     * exclusive lock.
     *
     * @param stamp a stamp returned by a write-lock operation
     * @throws IllegalMonitorStateException if the stamp does
     * not match the current state of this lock
     */
    public void unlockWrite(long stamp) {
        WNode h;
        if (state != stamp || (stamp & WBIT) == 0L)
            throw new IllegalMonitorStateException();
        state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
        if ((h = whead) != null && h.status != 0)
            release(h);
    }
  //释放任意匹配成功的锁
    /**
     * If the lock state matches the given stamp, releases the
     * corresponding mode of the lock.
     *
     * @param stamp a stamp returned by a lock operation
     * @throws IllegalMonitorStateException if the stamp does
     * not match the current state of this lock
     */
    public void unlock(long stamp) {


读,写 锁之间的转换


public long tryConvertToWriteLock(long stamp) {
     ..}
      public long tryConvertToWriteLock(long stamp) {
    ..}
    //释放读锁
    public void unlockRead(long stamp) {..}
    //释放写锁
    public void unlockWrite(long stamp) {..}


简单使用后#


/*
* StampedLock的简单使用
* */
public class StampedLock01 {
private  int balance;
StampedLock stamptedLock = new StampedLock();
//悲观读
public void read(){
    long s = stamptedLock.readLock();
try{
    try {
        System.out.println("拿到读锁"+Thread.currentThread().getName()+System.currentTimeMillis());
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}finally {
    stamptedLock.unlockRead(s);
}
}
// 乐观锁
public void OptimismRead(){
    //获取乐观锁,拿到 标记
    long stm = stamptedLock.tryOptimisticRead();
    try {
        System.out.println("一开始读取到的balance=="+balance);
        //方便测试     睡一会
        Thread.sleep(200);
        // 在读的时候,可能会出现现写操作-- 判断
        if(!stamptedLock.validate(stm)){  //1 锁空闲可用  2 拥有当前stm的线程获取到锁,返回true  其他返回false
            //重新读取
            long l = stamptedLock.readLock();
            System.out.println("乐观锁中发现了在读时,发现写操作,重新读结果为:  "+balance);
            // 更新标记, 用于锁的释放
            stm=l;
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        //释放锁
        stamptedLock.unlockRead(stm);
    }
}
// 带条件的读写锁
public void MyConditionReadWriteLock(int v){
    //判断balance是否符合更新的条件
    long stm = stamptedLock.readLock();
    try{
        // 为什么不会if  而用while
        while(stm>0){
            //转换成写锁;
            long s1 = stamptedLock.tryConvertToWriteLock(stm);
            if (s1!=0){// 成功转换
                balance+=v;
                System.out.println("进行写操作:  balance=="+balance+99);
                stm=s1;
                break;
            }else{  //没有转换成功
                //释放读锁
                stamptedLock.unlockRead(stm);
                // 获取写
                long s2 = stamptedLock.writeLock();
                stm=s2;
                System.out.println("手动获取写锁...");
            }
        }
    }finally {
        stamptedLock.unlock(stm);
    }
}
//独占的写
public void write(int v){
        long stamp = stamptedLock.writeLock();
        try{
            balance+=v;
            System.out.println("进行写的操作...结果:  "+balance+"  "+Thread.currentThread().getName()+"  "+"write中的标记为:"+stamp);
            Thread.sleep(2000);
        }catch (Exception e){
            e.printStackTrace();
        }
        finally {
            stamptedLock.unlockWrite(stamp);
        }
}
public static void main(String[] args) {
    StampedLock01 stampedLock01 = new StampedLock01();
    //测试普通的read  // 测试成功 异步执行
    //测试独占的写  成功
/*       new Thread(()->{
        // 乐观读
        stampedLock01.OptimismRead();
    }).start();
    new Thread(()->{
        stampedLock01.write(3);
    }).start();
*/
    new Thread(()->{
        stampedLock01.MyConditionReadWriteLock(1);
    }).start();
}
}


最后提一下 > 如何选择大名鼎鼎的AQS最有名的实现类ReentrantLocksynchronized这个JVM提供的锁,一开始synchronized是一个笨重的重量级锁,但是jdk1.5之后,进行了偏向锁,轻量级锁的优化,使它的性能和ReentrantLock擦不多了,于是没有特殊的要求,官方推荐使用synchronized

什么情况下使用ReentrantLock呢?


  • 使用它特有的公平锁
  • 使用它的Condition类,分组唤醒指定的线程
  • 提供了能够中断正在等待锁的线程的机制,lock.lockInterrupted()
相关文章
|
22天前
|
Java 开发者 C++
Java多线程同步大揭秘:synchronized与Lock的终极对决!
Java多线程同步大揭秘:synchronized与Lock的终极对决!
55 5
|
3月前
|
Java 开发者 C++
Java多线程同步大揭秘:synchronized与Lock的终极对决!
【6月更文挑战第20天】在Java多线程编程中,`synchronized`和`Lock`是两种关键的同步机制。`synchronized`作为内置关键字提供基础同步,简单但可能不够灵活;而`Lock`接口自Java 5引入,提供更复杂的控制和优化性能的选项。在低竞争场景下,`synchronized`性能可能更好,但在高并发或需要精细控制时,`Lock`(如`ReentrantLock`)更具优势。选择哪种取决于具体需求和场景,理解两者机制至关重要。
37 1
|
3月前
|
分布式计算 并行计算 安全
在Python Web开发中,Python的全局解释器锁(Global Interpreter Lock,简称GIL)是一个核心概念,它直接影响了Python程序在多线程环境下的执行效率和性能表现
【6月更文挑战第30天】Python的GIL是CPython中的全局锁,限制了多线程并行执行,尤其是在多核CPU上。GIL确保同一时间仅有一个线程执行Python字节码,导致CPU密集型任务时多线程无法充分利用多核,反而可能因上下文切换降低性能。然而,I/O密集型任务仍能受益于线程交替执行。为利用多核,开发者常选择多进程、异步IO或使用不受GIL限制的Python实现。在Web开发中,理解GIL对于优化并发性能至关重要。
52 0
|
3月前
|
Java 测试技术
Java多线程同步实战:从synchronized到Lock的进化之路!
【6月更文挑战第20天】Java多线程同步始于`synchronized`关键字,保证单线程访问共享资源,但为应对复杂场景,`Lock`接口(如`ReentrantLock`)提供了更细粒度控制,包括可重入、公平性及中断等待。通过实战比较两者在高并发下的性能,了解其应用场景。不断学习如`Semaphore`等工具并实践,能提升多线程编程能力。从同步起点到专家之路,每次实战都是进步的阶梯。
39 0
|
1月前
|
算法 Java
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
该博客文章综合介绍了Java并发编程的基础知识,包括线程与进程的区别、并发与并行的概念、线程的生命周期状态、`sleep`与`wait`方法的差异、`Lock`接口及其实现类与`synchronized`关键字的对比,以及生产者和消费者问题的解决方案和使用`Condition`对象替代`synchronized`关键字的方法。
JUC(1)线程和进程、并发和并行、线程的状态、lock锁、生产者和消费者问题
|
22天前
|
安全 Java 开发者
Java多线程同步:synchronized与Lock的“爱恨情仇”!
Java多线程同步:synchronized与Lock的“爱恨情仇”!
78 5
|
22天前
|
Java 开发者
揭秘!为什么大神都爱用Lock接口处理线程同步?
揭秘!为什么大神都爱用Lock接口处理线程同步?
37 5
|
22天前
|
Java
在Java多线程领域,精通Lock接口是成为高手的关键。
在Java多线程领域,精通Lock接口是成为高手的关键。相较于传统的`synchronized`,Lock接口自Java 5.0起提供了更灵活的线程同步机制,包括可中断等待、超时等待及公平锁选择等高级功能。本文通过实战演练介绍Lock接口的核心实现——ReentrantLock,并演示如何使用Condition进行精确线程控制,帮助你掌握这一武林秘籍,成为Java多线程领域的盟主。示例代码展示了ReentrantLock的基本用法及Condition在生产者-消费者模式中的应用,助你提升程序效率和稳定性。
18 2
|
22天前
|
Java 开发者
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选
在 Java 多线程编程中,Lock 接口正逐渐取代传统的 `synchronized` 关键字,成为高手们的首选。相比 `synchronized`,Lock 提供了更灵活强大的线程同步机制,包括可中断等待、超时等待、重入锁及读写锁等高级特性,极大提升了多线程应用的性能和可靠性。通过示例对比,可以看出 Lock 接口通过 `lock()` 和 `unlock()` 明确管理锁的获取和释放,避免死锁风险,并支持公平锁选择和条件变量,使其在高并发场景下更具优势。掌握 Lock 接口将助力开发者构建更高效、可靠的多线程应用。
18 2
|
22天前
|
Java
多线程同步新姿势:Lock接口助你“一统江湖”!
多线程同步新姿势:Lock接口助你“一统江湖”!
38 2