`
lindingyu
  • 浏览: 28654 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
文章分类
社区版块
存档分类
最新评论

JAVA线程同步2

 
阅读更多

       线程同步1中解决的是方法appendChar()在不同线程中改变StringBuilder对象而引发的不同步问题。进一步的问题是,如果现在有两个不同的方法A()和B(),他们在不同的线程中改变StringBuilder对象,这样引发的线程不同步该怎样避免? 

       解决上述问题的一种方法是线程中的沉睡(wait())和唤醒(notify())机制。同一时间要让方法A()和B()中的一个处于唤醒状态,一个处于沉睡状态。一个线程执行完毕,沉睡前唤醒另一个线程,如此往复,以保证线程的同步。

       这就涉及到了多线程同步问题中一个很经典的案例——生产者和消费者。生产者把生产出的产品放到容器中,逐渐累积。容器被放满时要停止生产,进入沉睡状态(阻塞队列)。当容器不满时,给生产线程信息,开始生产(阻塞队列→就绪队列)。消费者消耗容器中的产品,如果容器中的产品被耗尽,则进入沉睡状态(阻塞队列),停止消耗。当容器进入新产品时,给消费线程信息,开始消耗(阻塞队列→就绪队列)。

       代码示例如下:

public class ProduceAndConsumer {

	private StringBuilder sb = new StringBuilder();
    
	//生产——添加char
	public synchronized StringBuilder appendChar(char a,int id){
		//先判断容器是否装满
		while(sb.length()>=5){//自设定 长度到5为满	
		   try {
			  wait();//进入沉睡状态
		   } catch (InterruptedException e) {e.printStackTrace();}
		}		 
		sb.append(a);
		 System.out.println(id+",生产了:"+a +"  容器:"+sb);
		this.notify();//已加入产品,唤醒消费线程(如果沉睡)
		
		return sb;
	}
	
	//消费产品——删除char
	public synchronized char deleteChar(int id){
		//判断容器是否已耗尽
		while(sb.length()==0){
			try{
				wait();
			}catch(InterruptedException e){e.printStackTrace();}		
		}	
		char cDel = sb.charAt(0);
		sb.deleteCharAt(0);
		 System.out.println(id+",消费了:"+cDel +"  容器:"+sb);
		this.notify();
		
		return cDel;	
	}
	
	public static void main(String[] args){
		ProduceAndConsumer pac = new ProduceAndConsumer();		
		new Thread(new AppendThread(pac)).start();
		new Thread(new DeleteThread(pac)).start();		
	}
	
}
//生产线程
class AppendThread implements Runnable{
	
	ProduceAndConsumer pac;
	private char c=97;	
	
	public AppendThread(ProduceAndConsumer pac){
		this.pac = pac;
	}
	
	public void run(){	 
		for(int i=0;i<10;i++){			
		  pac.appendChar(c++,i);		 
	    }
	}
}
//消费线程
class DeleteThread implements Runnable{
	
	ProduceAndConsumer pac;
	
	public DeleteThread(ProduceAndConsumer pac){
		this.pac = pac;
	}
	
	public void run(){	 
		for(int i=0;i<10;i++){			
		  pac.deleteChar(i);		 
	    }
	}
}

    运行结果如下(每次运行的结果可能不同),传入的参数int id只用来打印标号:

        
     结果中,生产线程在id=4处生产e了,容器中变为abcde,容器充满(程序中设定最大长度为5),所以AppendThread线程进入沉睡状态,即进入阻塞队列。此时只运行DeleteThread消费线程。当消费线程在id=0处运行后,删除了a,容器又有了空间。此时又唤醒了AppendThread线程,即从阻塞队列进入就绪队列。由于只启动了两个线程,而且DeleteThread正在运行,所以AppendThread线程在就绪队列中不用等待,可以直接运行。后面的运行结果中,AppendThread线程和DeleteThread线程配合得当,容器中一直装有产品,且不满。两个线程都正常运行。

     这样就实现了两个方法A()和B()在不同线程中的同步。

  • 大小: 86.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics