多线程学习(六)-同步通信

线程同步通信:

  • Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。
  • 在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
  • 一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)


看一个例子,

子线程循环3次,接着主线程循环5,接着又回到子线程循环3次,接着再回到主线程又循环3,如此循环10次。

1.使用传统的线程同步通信方法,wait()和notify().

public class ThreadCommunication {
    public static void main(String[] args) throws InterruptedException {
        final Bussiness bussiness=new Bussiness();
        new Thread(new Runnable() {
            
            @Override
            public void run() {
    
                for (int i = 0; i <10; i++) {
                        bussiness.sub(i);    
                }
                
            }
        }).start();
        
            for (int i = 0; i <10; i++) {
                 bussiness.main(i);
        }
            
    }
}
class Bussiness{
    private boolean isSub=true;//用于判断是不是轮到子线程执行了
    synchronized void sub(int i){    //同步加锁,子线程循环中的一个线程执行的时候,其他的子线程不能执行
        if (!isSub) {
            try {
                this.wait();//通信,如果没轮到子线程执行,就等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int j = 0; j <3; j++) {
            System.out.println("sub.."+i+" thread of "+j+" loop");
        }
        isSub=false;//子线程执行完了,置为false,表示轮到主线程执行了
                this.notify();//线程通信,通知主线程执行
    }
    synchronized void main(int i){
        if (isSub) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        for (int j = 0; j < 5; j++) {
            System.out.println("main.."+i+" thread of "+j+" loop");
        }
        isSub=true;
        this.notify();
    }
}


2.使用java.util.concurrent.locks.Condition

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionCommunication {

    /**
     * @param args
     */
    public static void main(String[] args) {
        
        final Business business = new Business();
        new Thread(
                new Runnable() {
                    
                    @Override
                    public void run() {
                    
                        for(int i=1;i<=10;i++){
                            business.sub(i);
                        }
                        
                    }
                }
        ).start();
        
        for(int i=1;i<=10;i++){
            business.main(i);
        }
        
    }

    static class Business {
          Lock lock = new ReentrantLock();
         Condition condition = lock.newCondition();
          private boolean bShouldSub = true;
          public  void sub(int i){
              lock.lock();
              try{
                  while(!bShouldSub){
                      try {
                        condition.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                  }
                    for(int j=1;j<=3;j++){
                        System.out.println("sub thread sequence of " + j + ",loop of " + i);
                    }
                  bShouldSub = false;
                  condition.signal();//通知主线程
              }finally{
                  lock.unlock();
              }
          }
          
          public  void main(int i){
              lock.lock();
              try{
                 while(bShouldSub){
                          try {
                            condition.await();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                      }
                    for(int j=1;j<=5;j++){
                        System.out.println("main thread sequence of " + j + ",loop of " + i);
                    }
                    bShouldSub = true;
                    condition.signal();
          }finally{
              lock.unlock();
          }
      }
    
    }
}



看Condition类中有一个示例:

假定有一个绑定的缓冲区,它支持 puttake 方法。如果试图在空的缓冲区上执行take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行put 操作,则在有空间变得可用之前,线程将一直阻塞。

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 {
     lock.lock();
     try {
       while (count == items.length) //缓冲区满了,线程等待
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();//没满,通知取线程可以取了
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) //没有数据,不能取了,线程等待
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;//取完数据,可以通知写线程写了
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }



该示例用了两个condition,

notFullnotEmpty,这样取完时候只通知写的 线程,写完的时候只通知取的线程,如果只用一个condtion,比如有3个线程取数据,3个线程写数据,若其中一个取数据的线程完成取数据,此时缓冲队列没有数据了,这个时候应该通知写线程执行,插入数据,但是conditon.signal()的时候,会通知到另外5个线程中的一个,可能是取线程,也可能是写线程,如果是通知到了取线程,还是会等待,因为没有数据可以取,。

所以采用上面的两个conditon,如果出现上面描述的情况,只会通知到写线程,而不会通知取的线程,避免了这种情况的发生。



接下来看下面的这个例子:如果希望子线程1循环3次,接着子线程2执行4次,接着主线程循环5次,接着又回到子线程1循环3次,接着再回到子线程2又循环4,接着又到主线程中执行5次,如此循环10次。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreeConditionCommunication {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		final Business business = new Business();
		new Thread(
				new Runnable() {
					
					@Override
					public void run() {
					
						for(int i=1;i<=10;i++){
							business.sub2(i);
						}
						
					}
				}
		).start();
		new Thread(
				new Runnable() {
					
					@Override
					public void run() {
					
						for(int i=1;i<=10;i++){
							business.sub3(i);
						}
						
					}
				}
		).start();
		for(int i=1;i<=10;i++){
			business.main(i);
		}
		
	}

	static class Business {
		 Lock lock = new ReentrantLock();
		 Condition condition1 = lock.newCondition();
		 Condition condition2 = lock.newCondition();
		 Condition condition3 = lock.newCondition();
		  private int shouldSub = 1;//1表示轮到主线程1执行,2表示轮到sub2执行,3表示轮到sub3执行
		  public  void sub2(int i){
			  lock.lock();
			  try{
				  while(shouldSub!=2){//没轮到sub2执行,就等待
					  try {
						condition2.await();
					} catch (Exception e) {
						e.printStackTrace();
					}
				  }
					for(int j=1;j<=3;j++){
						System.out.println("sub2 thread sequence of " + j + ",loop of " + i);
					}
					shouldSub = 3;//sub2 执行完了,通知sub3执行
				    condition3.signal();
			  }finally{
				  lock.unlock();
			  }
		  }
		  public  void sub3(int i){
			  lock.lock();
			  try{
				  while(shouldSub!=3){//没轮到sub3执行,就等待
					  try {
						condition3.await();
					} catch (Exception e) {
						e.printStackTrace();
					}
				  }
					for(int j=1;j<=4;j++){
						System.out.println("sub3 thread sequence of " + j + ",loop of " + i);
					}
					shouldSub = 1;//sub2 执行完了,通知主线程执行
				    condition1.signal();
			  }finally{
				  lock.unlock();
			  }
		  }
		  public  void main(int i){
			  lock.lock();
			  try{
				 while(shouldSub!=1){//没轮到主线程执行,就等待
				  		try {
							condition1.await();
						} catch (Exception e) {
							e.printStackTrace();
						}
				  	}
					for(int j=1;j<=5;j++){
						System.out.println("main thread sequence of " + j + ",loop of " + i);
					}
					shouldSub = 2;//主线程执行完了,通知sub2执行
					condition2.signal();
		  }finally{
			  lock.unlock();
		  }
	  }
	
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值