J.U.C--locks--Condition
来源:互联网 发布:网络高清摄像机哪家好 编辑:程序博客网 时间:2024/06/05 17:29
首先来解释一下Condition有什么作用:Condition的作用和Java原生的通过synchronized与wait()和nitofy()/notifyAll()方法相结合实现等待/通知模型的作用是一样的。Condition是一个多线程间协调通信的工具类。
我觉得有个博客写得很好:http://ifeve.com/understand-condition/ 看着这个博客基本就能理解了。
我们都知道synchronized与wait()和nitofy()/notifyAll()方法相结合可以实现等待/通知模型,也可以实现生产者消费者模型。ReentrantLock同样可以,但是需要借助Condition,且Condition有更好的灵活性,具体体现在:
1)一个Lock里面可以创建多个Condition实例,实现多路通知。
2)notify()方法进行通知时,被通知的线程时Java虚拟机随机选择的,但是ReentrantLock结合Condition可以实现有选择性地通知,这是非常重要的。
1. Condition接口中的常用方法
1) void await()造成当前线程在接到信号或被中断之前一直处于等待状态。2)boolean await(long time, TimeUnit unit)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。3) long awaitNanos(long nanosTimeout)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。4) void awaitUninterruptibly()造成当前线程在接到信号之前一直处于等待状态。5) boolean awaitUntil(Date deadline)造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。6) void signal() 唤醒一个等待线程。7) void signalAll() 唤醒所有等待线程。
实例与解析
下面通过一个使用的实例来说明问题:
package thread;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ConditionDemo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) { Thread thread1 = new Thread(new Runnable(){ @Override public void run() { lock.lock(); System.out.println(Thread.currentThread().getName()+"正在运行。。。。"); try { Thread.sleep(2000); System.out.println(Thread.currentThread().getName()+"停止运行,等待一个signal"); condition.await();//等待 并且会释放锁 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"获得一个signal,继续执行"); lock.unlock(); } },"waitThread"); thread1.start(); try { Thread.sleep(1000);//保证线程1先执行,否则线程1将一直等待signal信号 } catch (InterruptedException e1) { e1.printStackTrace(); } Thread thread2 = new Thread(new Runnable(){ @Override public void run() { lock.lock();//获取锁 System.out.println(Thread.currentThread().getName()+"正在运行。。。。"); condition.signal();//发送信号,唤醒其它线程 System.out.println(Thread.currentThread().getName()+"发送一个signal"); System.out.println(Thread.currentThread().getName()+"发送一个signal后,结束"); lock.unlock();//释放锁之后,thread1再执行 } },"signalThread"); thread2.start(); }}
运行结果:
从运行结果我们来分析:
1)当thread1拿到锁之后开始执行,当调用condition.await()方法之后,thread1开始睡眠并释放锁。这里thread1会释放锁,这个是一定要知道的,其实Condition的内部也是AQS实现的,也是通过AQS维护的队列释放锁。注意在线程1和线程2之间加一个延时,保证线程1先获取锁。
2)thread1开始睡眠并释放锁之后,thread2拿到锁,拿到锁之后开始运行,并调用condition.signal()发射一个信号来唤醒正在等待此条件condition的线程。 (注意,这里线程2调用signal函数之后,线程1还没有被唤醒,必须等线程2释放锁之后,线程1才能够恢复,)。发射信号之后thread2会继续执行,执行完毕后thread2释放锁。
3)当thread2释放锁之后,thread1拿到锁开始继续运行直至结束。
上面的逻辑更加详细的来说;
我们知道AQS自己维护的队列是当前等待资源的队列,AQS会在资源被释放后,依次唤醒队列中从前到后的所有节点,使他们对应的线程恢复执行。直到队列为空。
而Condition自己也维护了一个队列,该队列的作用是维护一个等待signal信号的队列,两个队列的作用是不同,事实上,每个线程也仅仅会同时存在以上两个队列中的一个,流程是这样的:
线程1调用reentrantLock.lock时,线程被加入到AQS的等待队列中。
线程1调用await方法被调用时,该线程从AQS中移除,对应操作是锁的释放。
接着马上被加入到Condition的等待队列中,以为着该线程需要signal信号。
线程2,因为线程1释放锁的关系,被唤醒,并判断可以获取锁,于是线程2获取锁,并被加入到AQS的等待队列中。
线程2调用signal方法,这个时候Condition的等待队列中只有线程1一个节点,于是它被取出来,并被加入到AQS的等待队列中。 注意,这个时候, 线程1 并没有被唤醒。
signal方法执行完毕,线程2调用reentrantLock.unLock()方法,释放锁。这个时候因为AQS中只有线程1,于是,AQS释放锁后按从头到尾的顺序唤醒线程时,线程1被唤醒,于是线程1回复执行。
直到释放所整个过程执行完毕。
可以看到,整个协作过程是靠结点在AQS的等待队列和Condition的等待队列中来回移动实现的,Condition作为一个条件类,很好的自己维护了一个等待信号的队列,并在适时的时候将结点加入到AQS的等待队列中来实现的唤醒操作。
- J.U.C--locks--Condition
- “J.U.C”:Condition
- J.U.C--locks--ReentrantLock
- J.U.C.Locks 体系结构
- J.U.C之Condition
- “J.U.C”:Condition (r)
- J.U.C--locks--AQS分析
- J.U.C — Locks — ReentrantLock(一)
- J.U.C — Locks — ReentrantLock(二)
- 【Java并发编程实战】—–“J.U.C”:Condition
- 【Java并发编程实战】—–“J.U.C”:Condition
- 【Java并发编程实战】-----“J.U.C”:Condition
- 【死磕Java并发】-----J.U.C之Condition
- 【死磕Java并发】—–J.U.C之Condition
- J.U.C体系
- J.U.C
- “J.U.C”:ReentrantReadWriteLock
- J.U.C重入锁
- 神经网络ANN(一)
- jdk8
- JavaScript 带滴答声的时钟
- 蓝桥杯 小朋友排队(树状数组+逆序数)
- chrome 调试
- J.U.C--locks--Condition
- Prime Path(HDU-1973)
- activemq windows安装服务后 报1067 不能启动
- Python:目录和文件的操作模块os.path
- (Mysql 三)mysql的约束
- InfiniTAM艰辛的配置过程
- odd before even
- gulp开发简单配置以及配合browserify应用
- RxJava操作符——辅助操作符(Observable Utility Operators)