疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

Java多线程Condition详解


 

   

  Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

  先看一个关于Condition使用的简单实例:

  复制代码

  1 public class ConditionTest {

  2 public static void main(String[] args) {

  3 final Lock lock = new ReentrantLock();

  4 final Condition condition = lock.newCondition();

  5

  6 Thread thread1 = new Thread(new Runnable() {

  7 @Override

  8 public void run() {

  9 try {

  10 lock.lock();

  11 System.out.println("我需要等一个信号"+this);

  12 condition.await();

  13 System.out.println("我拿到一个信号"+this);

  14 } catch (Exception e) {

  15 // TODO: handle exception

  16 } finally{

  17 lock.unlock();

  18 }

  19

  20

  21 }

  22 }, "thread1");

  23 thread1.start();

  24 Thread thread2 = new Thread(new Runnable() {

  25 @Override

  26 public void run() {

  27 try {

  28 lock.lock();

  29 System.out.println("我拿到了锁");

  30 Thread.sleep(500);

  31 System.out.println("我发出一个信号");

  32 condition.signal();

  33 } catch (Exception e) {

  34 // TODO: handle exception

  35 } finally{

  36 lock.unlock();

  37 }

  38

  39

  40 }

  41 }, "thread2");

  42 thread2.start();

  43 }

  44 }

  复制代码

  运行结果:

  1 我需要等一个信号com.luchao.traditionalthread.ConditionTest$1@10bc3c9

  2 我拿到了锁

  3 我发出一个信号

  4 我拿到一个信号com.luchao.traditionalthread.ConditionTest$1@10bc3c9

  可以看到,Condition的执行方式,是当在线程1中调用await方法后,线程1将释放锁,并且将自己沉睡,等待唤醒,线程2获取到锁后,开始做事,完毕后,调用Condition的signal方法,唤醒线程1,线程1恢复执行。

  以上说明Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。

  Condition与传统线程通信有些类似,它的使用更广,可以将多个线程进行通信,以完成更加复杂的通信。

  用Condition替换传统线程通信,在前面的传统有一个子线程和主线程交替运行50次的实例,使用Condition也可以完成。

  代码如下:

  复制代码

  1 public class ConditionCommuniction {

  2 public static void main(String[] args) {

  3 final Business business = new Business();

  4 new Thread(new Runnable() {

  5 @Override

  6 public void run() {

  7 for (int i = 0; i < 50; i++) {

  8 business.sub(i);

  9 }

  10 }

  11 }).start();

  12 for (int i = 0; i < 50; i++) {

  13 business.main(i);

  14 }

  15 }

  16

  17

  18 static class Business{

  19 private Lock lock = new ReentrantLock();

  20 private boolean isMain = true;

  21 private Condition condition = lock.newCondition();

  22 public void main(int i){

  23 lock.lock();

  24 try {

  25 while(!isMain){

  26 condition.await();

  27 }

  28 for (int j = 0; j < 100; j++) {

  29 System.out.println("main is looping :" + j +" in " + i);

  30 }

  31 isMain = false;

  32 condition.signal();

  33 } catch (Exception e) {

  34 // TODO: handle exception

  35 } finally{

  36 lock.unlock();

  37 }

  38 }

  39 public void sub(int i){

  40 lock.lock();

  41 try {

  42 while(isMain){

  43 condition.await();

  44 }

  45 for (int j = 0; j < 10; j++) {

  46 System.out.println("sub is looping :" + j +" in " + i);

  47 }

  48 isMain = true;

  49 condition.signal();

  50 } catch (Exception e) {

  51 // TODO: handle exception

  52 } finally{

  53 lock.unlock();

  54 }

  55 }

  56 }

  57 }

  复制代码

  在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。

  这样看来,Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition,下面引入API中的一段代码,加以说明。

  复制代码

  1 class BoundedBuffer {

  2 final Lock lock = new ReentrantLock();//锁对象

  3 final Condition notFull = lock.newCondition();//写线程条件

  4 final Condition notEmpty = lock.newCondition();//读线程条件

  5

  6 final Object[] items = new Object[100];//缓存队列

  7 int putptr/*写索引*/, takeptr/*读索引*/, count/*队列中存在的数据个数*/;

  8

  9 public void put(Object x) throws InterruptedException {

  10 lock.lock();

  11 try {

  12 while (count == items.length)//如果队列满了

  13 notFull.await();//阻塞写线程

  14 items[putptr] = x;//赋值

  15 if (++putptr == items.length) putptr = 0;//如果写索引写到队列的最后一个位置了,那么置为0

  16 ++count;//个数++

  17 notEmpty.signal();//唤醒读线程

  18 } finally {

  19 lock.unlock();

  20 }

  21 }

  22

  23 public Object take() throws InterruptedException {

  24 lock.lock();

  25 try {

  26 while (count == 0)//如果队列为空

  27 notEmpty.await();//阻塞读线程

  28 Object x = items[takeptr];//取值

  29 if (++takeptr == items.length) takeptr = 0;//如果读索引读到队列的最后一个位置了,那么置为0

  30 --count;//个数--

  31 notFull.signal();//唤醒写线程

  32 return x;

  33 } finally {

  34 lock.unlock();

  35 }

  36 }

  37 }

  复制代码

  这就是多个Condition的强大之处,假设缓存队列中已经存满,那么阻塞的肯定是写线程,唤醒的肯定是读线程,相反,阻塞的肯定是读线程,唤醒的肯定是写线程,那么假设只有一个Condition会有什么效果呢,缓存队列中已经存满,这个Lock不知道唤醒的是读线程还是写线程了,如果唤醒的是读线程,皆大欢喜,如果唤醒的是写线程,那么线程刚被唤醒,又被阻塞了,这时又去唤醒,这样就浪费了很多时间。

  将上面主线程和子线程交替运行的程序进行扩展,三个线程交替运行,代码如下:

  复制代码

  1 public class ThreeConditionCommunication {

  2 public static void main(String[] args) {

  3 final Business business = new Business();

  4 new Thread(new Runnable() {

  5

  6 @Override

  7 public void run() {

  8 for (int i = 0; i < 50; i++) {

  9 business.sub1(i);

  10 }

  11 }

  12 }).start();

  13 new Thread(new Runnable() {

  14

  15 @Override

  16 public void run() {

  17 for (int i = 0; i < 50; i++) {

  18 business.sub2(i);

  19 }

  20 }

  21 }).start();

  22 for (int i = 0; i < 50; i++) {

  23 business.main(i);

  24 }

  25 }

  26 static class Business{

  27 Lock lock = new ReentrantLock();

  28 Condition main = lock.newCondition();

  29 Condition sub1 = lock.newCondition();

  30 Condition sub2 = lock.newCondition();

  31 int runNum = 1;

  32

  33 public void main(int i){

  34 lock.lock();

  35 try {

  36 while(runNum!=1){

  37 main.await();//主线程等待

  38 }

  39 for (int j = 0; j < 100; j++) {

  40 System.out.println("main is looping of "+j+" in "+i);

  41 }

  42 runNum = 2;

  43 sub1.signal();//唤醒子线程1

  44 } catch (Exception e) {

  45 // TODO: handle exception

  46 } finally{

  47 lock.unlock();

  48 }

  49 }

  50 public void sub1(int i){

  51 lock.lock();

  52 try {

  53 while(runNum!=2){

  54 sub1.await();//子线程1等待

  55 }

  56 for (int j = 0; j < 10; j++) {

  57 System.out.println("sub1 is looping of "+j+" in "+i);

  58 }

  59 runNum = 3;

  60 sub2.signal();//唤醒子线程2

  61 } catch (Exception e) {

  62 // TODO: handle exception

  63 } finally{

  64 lock.unlock();

  65 }

  66 }

  67 public void sub2(int i){

  68 lock.lock();

  69 try {

  70 while(runNum!=3){

  71 sub2.await();//子线程2等待

  72 }

  73 for (int j = 0; j < 20; j++) {

  74 System.out.println("sub2 is looping of "+j+" in "+i);

  75 }

  76 runNum = 1;

  77 main.signal();//唤醒主线程

  78 } catch (Exception e) {

  79 // TODO: handle exception

  80 } finally{

  81 lock.unlock();

  82 }

  83 }

  84 }

  85 }

  复制代码

  由此可见,Condition在多线程通信的强大作用,可以大大提高程序效率。