• Java并发编程 之 同步队列与等待队列


    原文地址:Java并发编程 之 同步队列与等待队列,取得同意后转载
    在上一篇博客中,我简单的介绍了对Condition和ReentrantLock的使用,但是想要更好的掌握多线程编程,单单会用是不够的。这篇我会针对Condition方法中的await和signal的实现原理来梳理一下我的理解。

    首先我们需要了解同步队列和等待队列的概念。简单的理解是同步队列存放着竞争同步资源的线程的引用(不是存放线程),而等待队列存放着待唤醒的线程的引用。

    同步队列中存放着一个个节点,当线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点并将其加入同步队列,首节点表示的获取同步状态成功的线程节点。

    在这里插入图片描述
    Condition维护着一个等待队列与同步队列相似。主要针对await和signal的操作。
    在这里插入图片描述

    现在不理解没关系,下面用实例来认识一下它们在多线程中是如何使用的。这里实现了三个多线程的run方法。A线程输出A然后通知B,然后B通知C。

    public static class ThreadA extends Thread{
            @Override
            public void run(){
                try{
                    lock.lock();
                    System.out.println("A进程输出" + " : " + ++index);
                    conditionB.signal();
                    conditionA.await();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    
        public static class ThreadB extends Thread{
            @Override
            public void run(){
                try{
                    lock.lock();
                    System.out.println("B进程输出" + " : " + ++index);
                    conditionC.signal();
                    conditionB.await();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    
        public static class ThreadC extends Thread{
            @Override
            public void run(){
                try{
                    lock.lock();
                    System.out.println("C进程输出" + " : " + ++index);
                    conditionA.signal();
                    conditionC.await();
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }
    
    public class CondtionTest {
    
        public static ReentrantLock lock = new ReentrantLock();
        public static Condition conditionA = lock.newCondition();
        public static Condition conditionB = lock.newCondition();
        public static Condition conditionC = lock.newCondition();
        public static int index = 0;
        public static void main(String[] args){
            ThreadA threadA = new ThreadA();
            ThreadB threadB = new ThreadB();
            ThreadC threadC = new ThreadC();
    
            threadA.start();//(1)
            threadB.start();//(2)
            threadC.start();//(3)
        }
    }
    

    当(1)(2)(3)三个线程被调用时,因为三个线程同时竞争lock,这里假设线程A拿到了lock(线程A虽然是看起来是先start(),但是正在的调用还是看调度程序的,所以这里只能假设是A线程拿到同步资源)。首节点表示的是正在操作同步资源的线程。所以现在的同步队列是:
    在这里插入图片描述

    接着线程A输出了:“A进程输出 : 1”。然后调用conditionB.signal(),其实这一步的signal是没什么意义的,因为conditionB现在没有线程是可以被唤醒的。
    当conditionA.await()被执行到的时候,线程A同步队列中被移除,对应操作是锁的释放; 线程A(节点A)接着被加入到ConditionA等待队列,因为线程需要singal信号。

    同步队列
    在这里插入图片描述

    A等待队列
    在这里插入图片描述

    现在在同步队列中的首节点是B节点,那么B线程占用了同步资源就可以开始运行了。先是输出“B进程输出 : 2”,同样的signal操作也是没有意义的,因为conditionC是没有可以被唤醒的线程。当conditionB.await()被执行到的时候,线程B同步队列中被移除,线程B(节点B)接着被加入到ConditionB等待队列

    同步队列
    在这里插入图片描述

    B等待队列
    在这里插入图片描述

    终于轮到了C线程占用同步资源了,再输出“C进程输出:3”之后,调用conditionA.signal(),注意这个signal是有用的
    因为在conditionA的等待队列中A线程是在等待的,把它取出来加入到同步队列中去竞争,但是这个时候线程A还没唤醒。首节点还是C

    同步队列
    在这里插入图片描述

    接着conditionC.await()被执行。线程C同步队列中被移除,线程C(节点C)接着被加入到ConditionC等待队列

    同步队列
    在这里插入图片描述

    C等待队列
    在这里插入图片描述

    注意到同步队列中的首节点已经变回了节点A了。所以线程A在刚刚等待的地方继续执行,最后释放了lock。但是线程B和线程C最后也没有其他线程去唤醒,状态一直为WAITING,而线程A的状态为TERMINATED。

  • 相关阅读:
    vue中的echarts实现宽度自适应
    前端执行vue打包后的dist文件
    nvm的使用和nrm的使用
    element-ui 中让el-container 高度自适应
    QQ登录报错:redirect uri is illegal(100010)
    纯CSS实现table固定thead,tbody进行滚动.html
    js实现垂直向上滚动
    我的 vscode 配置文件!
    CSS实现水平垂直居中的6种方式!
    百度API获取地点经纬度
  • 原文地址:https://www.cnblogs.com/Sherlock-J/p/12925938.html
Copyright © 2020-2023  润新知