zoukankan      html  css  js  c++  java
  • java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)

    转载自https://www.cnblogs.com/jalja/p/5895051.html

    一、Condition 类

      在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.concurrent.locks.ReentrantLock 锁,JDK也为我们提供了与此功能相应的类java.util.concurrent.locks.Condition。Condition与重入锁是通过lock.newCondition()方法产生一个与当前重入锁绑定的Condtion实例,我们通知该实例来控制线程的等待与通知。该接口的所有方法:

    复制代码
    public interface Condition {
         //使当前线程加入 await() 等待队列中,并释放当锁,当其他线程调用signal()会重新请求锁。与Object.wait()类似。
        void await() throws InterruptedException;
    
        //调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
        //调用该方法后,结束等待的唯一方法是其它线程调用该条件对象的signal()或signalALL()方法。等待过程中如果当前线程被中断,该方法仍然会继续等待,同时保留该线程的中断状态。 
        void awaitUninterruptibly();
    
        // 调用该方法的前提是,当前线程已经成功获得与该条件对象绑定的重入锁,否则调用该方法时会抛出IllegalMonitorStateException。
        //nanosTimeout指定该方法等待信号的的最大时间(单位为纳秒)。若指定时间内收到signal()或signalALL()则返回nanosTimeout减去已经等待的时间;
        //若指定时间内有其它线程中断该线程,则抛出InterruptedException并清除当前线程的打断状态;若指定时间内未收到通知,则返回0或负数。 
        long awaitNanos(long nanosTimeout) throws InterruptedException;
    
        //与await()基本一致,唯一不同点在于,指定时间之内没有收到signal()或signalALL()信号或者线程中断时该方法会返回false;其它情况返回true。
        boolean await(long time, TimeUnit unit) throws InterruptedException;
    
       //适用条件与行为与awaitNanos(long nanosTimeout)完全一样,唯一不同点在于它不是等待指定时间,而是等待由参数指定的某一时刻。
        boolean awaitUntil(Date deadline) throws InterruptedException;
        
        //唤醒一个在 await()等待队列中的线程。与Object.notify()相似
        void signal();
    
       //唤醒 await()等待队列中所有的线程。与object.notifyAll()相似
        void signalAll();
    }
    复制代码

    二、使用

    1、await()  等待  与 singnal()通知

    复制代码
     1 package com.jalja.org.base.Thread;
     2 
     3 import java.util.concurrent.TimeUnit;
     4 import java.util.concurrent.locks.Condition;
     5 import java.util.concurrent.locks.ReentrantLock;
     6 
     7 /**
     8  * Condition 配合Lock  实现线程的等待 与通知
     9  */
    10 public class ConditionTest{
    11     public static ReentrantLock lock=new ReentrantLock();
    12     public static Condition condition =lock.newCondition();
    13     public static void main(String[] args) {
    14         new Thread(){
    15             @Override
    16             public void run() {
    17                 lock.lock();//请求锁
    18                 try{
    19                     System.out.println(Thread.currentThread().getName()+"==》进入等待");
    20                     condition.await();//设置当前线程进入等待
    21                 }catch (InterruptedException e) {
    22                     e.printStackTrace();
    23                 }finally{
    24                     lock.unlock();//释放锁
    25                 }
    26                 System.out.println(Thread.currentThread().getName()+"==》继续执行");
    27             }    
    28         }.start();
    29         new Thread(){
    30             @Override
    31             public void run() {
    32                 lock.lock();//请求锁
    33                 try{
    34                     System.out.println(Thread.currentThread().getName()+"=》进入");
    35                     Thread.sleep(2000);//休息2秒
    36                     condition.signal();//随机唤醒等待队列中的一个线程
    37                     System.out.println(Thread.currentThread().getName()+"休息结束");
    38                 }catch (InterruptedException e) {
    39                     e.printStackTrace();
    40                 }finally{
    41                     lock.unlock();//释放锁
    42                 }
    43             }    
    44         }.start();
    45     }
    46 }
    复制代码

    执行结果:

    Thread-0==》进入等待
    Thread-1=》进入
    Thread-1休息结束
    Thread-0==》继续执行

    流程:在调用await()方法前线程必须获得重入锁(第17行代码),调用await()方法后线程会释放当前占用的锁。同理在调用signal()方法时当前线程也必须获得相应重入锁(代码32行),调用signal()方法后系统会从condition.await()等待队列中唤醒一个线程。当线程被唤醒后,它就会尝试重新获得与之绑定的重入锁,一旦获取成功将继续执行。所以调用signal()方法后一定要释放当前占用的锁(代码41行),这样被唤醒的线程才能有获得锁的机会,才能继续执行。

    三、JDK中对Condition 的使用

      我们来看看java.util.concurrent.ArrayBlockingQueue;

      基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置。

      看看他的put方法:

    复制代码
      public void put(E e) throws InterruptedException {
            checkNotNull(e);//对传入元素的null判断
            final ReentrantLock lock = this.lock;
            lock.lockInterruptibly();//对put()方法做同步
            try {
                while (count == items.length)//如果队列已满
                    notFull.await();//让当前添加元素的线程进入等待状态
                insert(e);// 如果有其他线程调用signal() 通知该线程 ,则进行添加行为
            } finally {
                lock.unlock();//释放锁
            }
        }
        
        private E extract() {
            final Object[] items = this.items;
            E x = this.<E>cast(items[takeIndex]);
            items[takeIndex] = null;
            takeIndex = inc(takeIndex);
            --count;
            notFull.signal();//唤醒一个在Condition等待队列中的线程
            return x;
        }
    复制代码
    每天用心记录一点点。内容也许不重要,但习惯很重要!
     
     
     
     

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jiq408694711/article/details/51052592


    是否释放锁:调用sleep和yield的时候不释放当前线程所获得的锁,但是调用await/wait的时候却释放了其获取的锁并阻塞等待。

    调用后何时恢复:

    # sleep让线程阻塞,且在指定的时间之内都不会执行,时间到了之后恢复到就绪状态,也不一定被立即调度执行;

    # yield只是让当前对象回到就绪状态,还是有可能马上被再次被调用执行。

    # await/wait,它会一直阻塞在条件队列之上,之后某个线程调用对应的notify/signal方法,才会使得await/wait的线程回到就绪状态,也是不一定立即执行。

    谁的方法:yield和sleep方法都是Thread类的,而wait方法是Object类的,await方法是Condition显示条件队列的。

    执行环境:yield和sleep方法可以放在线程中的任意位置,而await/wait方法必须放在同步块里面,否则会产生运行时异常。

    await/wait

    Sleep

    Yield

    是否释放持有的锁

    释放

    不释放

    不释放

    调用后何时恢复

    唤醒后进入就绪态

    指定时间后

    立刻进入就绪态

    谁的方法

    Condition/Object

    Thread

    Thread

    执行环境

    同步代码块

    任意位置

    任意位置
    ---------------------
    作者:小弟季义钦
    来源:CSDN
    原文:https://blog.csdn.net/jiyiqinlovexx/article/details/51052592
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    video 属性和事件用法大全
    微信小程序 组件通信相关知识整理
    JavaScript实现登录窗口的拖拽
    JS 各种宽高
    CSS3 Animation
    CSS3 Transition
    CSS3 Transform
    vue 回到页面顶部
    element-ui 动态换肤
    Chrome浏览器下自动填充的输入框背景
  • 原文地址:https://www.cnblogs.com/heroinss/p/9917796.html
Copyright © 2011-2022 走看看