java线程并发控制:ReentrantLock Condition使用详解
2015-12-19 11:00
417 查看
本文摘自:http://outofmemory.cn/java/java.util.concurrent/lock-reentrantlock-condition
java的java.util.concurrent.locks包内有Condition接口,该接口的官方定义如下:
Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to "wait") until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like
我们通过一个实际的例子来解释Condition的用法:
我们要打印1到9这9个数字,由A线程先打印1,2,3,然后由B线程打印4,5,6,然后再由A线程打印7,8,9. 这道题有很多种解法,现在我们使用Condition来做这道题(使用Object的wait,notify方法的解法在这里)。
代码:
View Code
上述代码中有完整的注释,请参考注释,理解Condition的用法。
基本思路就是首先要A线程先写1,2,3,这时候B线程应该等待reachThredCondition信号,而当A线程写完3之后就通过signal告诉B线程“我写到3了,该你了”,这时候A线程要等嗲reachSixCondition信号,同时B线程得到通知,开始写4,5,6,写完4,5,6之后B线程通知A线程reachSixCondition条件成立了,这时候A线程就开始写剩下的7,8,9了。
为了更好的理解Condition的用法,我们再看下java官方提供的例子:
这个示例中BoundedBuffer是一个固定长度的集合,这个在其put操作时,如果发现长度已经达到最大长度,那么会等待notFull信号,如果得到notFull信号会像集合中添加元素,并发出notEmpty的信号,而在其take方法中如果发现集合长度为空,那么会等待notEmpty的信号,同时如果拿到一个元素,那么会发出notFull的信号。
java的java.util.concurrent.locks包内有Condition接口,该接口的官方定义如下:
Conditionfactors out the
Objectmonitor methods (
wait,
notifyand
notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary
Lockimplementations. Where a
Lockreplaces the use of
synchronizedmethods and statements, a
Conditionreplaces the use of the Object monitor methods.
Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to "wait") until notified by another thread that some state condition may now be true. Because access to this shared state information occurs in different threads, it must be protected, so a lock of some form is associated with the condition. The key property that waiting for a condition provides is that it atomically releases the associated lock and suspends the current thread, just like
Object.wait.
我们通过一个实际的例子来解释Condition的用法:
我们要打印1到9这9个数字,由A线程先打印1,2,3,然后由B线程打印4,5,6,然后再由A线程打印7,8,9. 这道题有很多种解法,现在我们使用Condition来做这道题(使用Object的wait,notify方法的解法在这里)。
代码:
package cn.outofmemory.locks; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class App { static class NumberWrapper { public int value = 1; } public static void main(String[] args) { //初始化可重入锁 final Lock lock = new ReentrantLock(); //第一个条件当屏幕上输出到3 final Condition reachThreeCondition = lock.newCondition(); //第二个条件当屏幕上输出到6 final Condition reachSixCondition = lock.newCondition(); //NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final //注意这里不要用Integer, Integer 是不可变对象 final NumberWrapper num = new NumberWrapper(); //初始化A线程 Thread threadA = new Thread(new Runnable() { @Override public void run() { //需要先获得锁 lock.lock(); try { System.out.println("threadA start write"); //A线程先输出前3个数 while (num.value <= 3) { System.out.println(num.value); num.value++; } //输出到3时要signal,告诉B线程可以开始了 reachThreeCondition.signal(); } finally { lock.unlock(); } lock.lock(); try { //等待输出6的条件 reachSixCondition.await(); System.out.println("threadA start write"); //输出剩余数字 while (num.value <= 9) { System.out.println(num.value); num.value++; } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }); Thread threadB = new Thread(new Runnable() { @Override public void run() { try { lock.lock(); while (num.value <= 3) { //等待3输出完毕的信号 reachThreeCondition.await(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } try { lock.lock(); //已经收到信号,开始输出4,5,6 System.out.println("threadB start write"); while (num.value <= 6) { System.out.println(num.value); num.value++; } //4,5,6输出完毕,告诉A线程6输出完了 reachSixCondition.signal(); } finally { lock.unlock(); } } }); //启动两个线程 threadB.start(); threadA.start(); } }
View Code
上述代码中有完整的注释,请参考注释,理解Condition的用法。
基本思路就是首先要A线程先写1,2,3,这时候B线程应该等待reachThredCondition信号,而当A线程写完3之后就通过signal告诉B线程“我写到3了,该你了”,这时候A线程要等嗲reachSixCondition信号,同时B线程得到通知,开始写4,5,6,写完4,5,6之后B线程通知A线程reachSixCondition条件成立了,这时候A线程就开始写剩下的7,8,9了。
为了更好的理解Condition的用法,我们再看下java官方提供的例子:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class AppOfficial { /** * BoundedBuffer 是一个定长100的集合,当集合中没有元素时,take方法需要等待,直到有元素时才返回元素 * 当其中的元素数达到最大值时,要等待直到元素被take之后才执行put的操作 * @author paopao * */ static class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count;//增指针,减指针,全局变量计数器 public void put(Object x) throws InterruptedException { System .out.println("put wait lock"); lock.lock(); System.out.println("put get lock"); try { while (count == items.length) { //数组填充满时,put线程放弃CPU执行权,等待 System.out.println("buffer full, please wait"); notFull.await(); } items[putptr] = x; if (++putptr == items.length) //循环移位放数据 putptr = 0; ++count; //count作为增计数器,为了判断是否达到数组最大下限值:items.length notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { System.out.println("take wait lock"); lock.lock(); System.out.println("take get lock"); try { while (count == 0) { //数组为0时,停止去数据,take线程await System.out.println("no elements, please wait"); notEmpty.await(); } Object x = items[takeptr]; if (++takeptr == items.length)//循环移位取数据 takeptr = 0; --count; //count作为减计数器,为了判断是否减少到数组最小下限值:0 System.out.println("取出数据时,数组下标为:"+takeptr); notFull.signal(); return x; } finally { lock.unlock(); } } } public static void main(String[] args) { final BoundedBuffer boundedBuffer = new BoundedBuffer(); Thread t1 = new Thread(new Runnable() { //@Override public void run() { System.out.println("t1 run"); for (int i=0;i<1000;i++) { try { System.out.println("putting.."); boundedBuffer.put(Integer.valueOf(i)); } catch (InterruptedException e) { e.printStackTrace(); } } } }) ; Thread t2 = new Thread(new Runnable() { //@Override public void run() { for (int i=0;i<1000;i++) { try { Object val = boundedBuffer.take(); System.out.println(val); } catch (InterruptedException e) { e.printStackTrace(); } } } }) ; t1.start(); t2.start(); } }
这个示例中BoundedBuffer是一个固定长度的集合,这个在其put操作时,如果发现长度已经达到最大长度,那么会等待notFull信号,如果得到notFull信号会像集合中添加元素,并发出notEmpty的信号,而在其take方法中如果发现集合长度为空,那么会等待notEmpty的信号,同时如果拿到一个元素,那么会发出notFull的信号。
相关文章推荐
- Java RMI学习
- javabean和json的常用转换解析
- Java编程思想笔记02:组合与继承、final、策略设计模式与适配器模式、内部类、序列化控制(注意事项)
- maven的学习之路——intellij的maven1.5JDK问题
- java中构造函数的特点
- Java算法分析1—————寻找数组同样元素
- Struts2之异常处理
- Java变量类型,实例变量 与局部变量 静态变量
- java线程(生产者/消费者)
- ubuntu14.04安装jdk
- Struts2概述及与Struts1的对比
- Struts2概述及与Struts1的对比
- 深入理解Java:注解(Annotation)自定义注解入门
- Java输出xml文件——DOM法写入《Dota…
- Java读取xml文件——DOM解析《Dota2…
- 【Java EE 学习 75 下】【数据采集系统第七天】【二进制运算实现权限管理】【使用反射初始化权限表】【权限捕获拦截器动态添加权限】
- 菜鸟猿大战Java之集合框架系列(二)
- spring mvc拦截器
- java中字节数组与字符串,与数据之间的装换
- Eclipse中10个最有用的快捷键