java多线程设计模式之Producer-Consumer模式(二)

   延伸理解:InterruptedException

InterruptedException表明该方法中可能会抛出这个异常,其包含两种含义,这是一个花费时间的方法,同时这个也是可以取消的方法。用一句话来概括就是:这个方法可能会花费时间,但是同时也是可以取消的。参考java的标准类库,可以发现典型的方法有以下几个:

1.java.lang.Object类的wait()方法

2.java.lang.Thread类的sleep()方法

3.java.lang.Thread类的join()方法

花费时间的方法线程执行wait()方法过后会进入等待队列,等待被notify/notifyAll方法来唤醒,在等待期间线程是不运行的,但是需要一定的时间来等待,等待所提到的notify/notifyAll方法,而调用sleep方法时,则会暂停执行线程,这也是花费时间的方法,同样,执行join方法时,这个方法则会等待指定的线程结束之后才去启动要加入的线程,其需要等待指定的线程结束。

取消wait,sleep,join的方法:

1interrupt方法:假设Thread.sleep(604800000)方法则是等待7天,这时候假设是在Alice线程中执行的方法,那么如果运行到这句,则会使的Alice线程处于等待的状态,所以,我们可以通过alice.Interrupt()来取消等待的状态。当执行interrupt方法时不需要获取线程的实例的锁,无论何时,任何线程都i可以调用其他线程的interrupt的方法。调用后就会抛出InterruptedException,此时抛出异常线程为alice。如果在wait()方法中使用interrupt方法时需要注意锁的问题,线程进入等待队列时已经释放了锁,当正在wait() 的线程被调用interrupt方法时,该线程会在重新获取锁之后,抛出InterruptedException异常。

notify方法和interrupt方法的不同:

notify/notifyAll方法时java.lang.Object类的方法,唤醒的是等待队列中的线程,而不是直接指定的线程,唤醒的线程会继续执行wait()方法的下一语句,其在执行notiy/notifyAll方法时必须获取实例的锁。interrupt方法是java.lang.Thread类的方法,可以直接指定线程,并将其唤醒,当被interrupt的线程处于wait()或者是sleep()时,会抛出InterruptedException异常,执行此方法时不需要获取要取消线程的锁。

检查中断状态的方法--isInterrupted(),此方法可以用来检查指定的线程是否处于中断状态,中断则为true,未中断则为false;Thread.interrupted()方法则是检查并且清除线程的中断状态,然而,这个方法只能用于当前的线程,不能被其它线程直接调用,并不能用于清除其他线程的中断状态。

     java.util.concurrent包和Producer-Consumer模式:


ArrayBlocingQueue是基于数组的BlockingQueue该类基于数组,其很类似于上节设计的Table类,当数组满了仍要put时或者是数组为空时仍要take时就会阻塞。

LinkedBlockingQueue是基于链表的BlockingQueue,其最大的特点是大小没有限制。

PriorityBlockingQueue是带有优先级的BlockingQueue,其是根据Comparable接口自然排序,或者构造函数的Comparator接口决定的顺序。

DelayQueue时一定时间之后才可以调用take方法的BlockingQueue,其保存的是,java.util.concurrent.Delayed对象的队列。只有在各元素指定的时间到期后才可以take。到期时间最长的元素将先被take方法取得。

SynchronousQueue类,直接传递BlockingQueue,相当于将Channel去掉,将Producer和Consumer结合在一起,进行直接传递。

ConcurrentLinkedQueue类,元素个数没有最大限制的线程安全队列,其并不是BlockingQueue类的实现类,他表示的是元素的个数没有最大限制,同时,内部的数据结构是分开的,线程之间不会互相影响,所以也就无需进行互斥处理。


例子:使用java.util.concurrent.ArrayBlockingQueue来重新设计Table类:

public class Table extends ArrayBlockingQueue<String> {
	public Table (int count){
		super(count);
	}
	
	public void put(String cake) throws InterruptedException{
		System.out.println(Thread.currentThread().getName()+" puts "+cake);
		super.put(cake);
	}
	
	public String take() throws InterruptedException{
		String cake = super.take();
		System.out.println(Thread.currentThread().getName()+" takes " + cake);
		return cake;
	}

}
由于ArrayBlockingQueue类是一个泛型类,需要指定其添加的元素的类型,由于其已经实现了对于队列中的元素位置的控制,在这里就不在表示蛋糕的循环位置。执行效果同原来的示例相同。

延伸一下:使用java.util.concurrent.Exchanger类交换缓冲区:

ProducerThread类:

public class ProducerThread extends Thread {
	private final Exchanger<char[]> exchanger;
	private final Random random;
	private char[] buffer=null;
	private char index=0;
	
	public ProducerThread(Exchanger<char[]> exchanger,char[] buffer,long seed){
		super("ProducerThread");
		this.exchanger = exchanger;
		this.buffer=buffer;
		this.random = new Random(seed);
	}
	
	public void run(){
		try{
			while(true){
				for(int i=0;i<buffer.length;i++){
					buffer[i] = nextChar();
					System.out.println(Thread.currentThread().getName()+":"
							+buffer[i] + "->");
									
				}
				//交换缓冲区
				System.out.println(Thread.currentThread().getName() + ": BEFORE exchanger");
				buffer = exchanger.exchange(buffer);
				System.out.println(Thread.currentThread().getName() + ": AFTER exchanger");
			}
		}catch(InterruptedException e){
			
		}
	}
	
	//产生字符的函数
	public char nextChar() throws InterruptedException{
		char c = (char) ('A'+index % 26);
		index++;
		Thread.sleep(random.nextInt(1000));
		return c;
	}

}
ConsumerThread类:

public class ConsumerThread extends Thread {
	private final Exchanger<char[]> exchanger;
	private final Random random;
	private char[] buffer=null;
	
	public ConsumerThread(Exchanger<char[]> exchanger ,char[] buffer,long seed){
		super("ConsumerThread");
		this.exchanger=exchanger;
		this.buffer=buffer;
		this.random= new Random(seed);
	}
	
	public void run(){
		try{
			System.out.println(Thread.currentThread().getName()+": BEFORE exchange");
			buffer=exchanger.exchange(buffer);
			System.out.println(Thread.currentThread().getName()+": AFTER exchange");
			
			//从缓冲区取出字符
			for(int i=0;i<buffer.length;i++){
				System.out.println(Thread.currentThread().getName()+": ->" +buffer[i]);
				Thread.sleep(random.nextInt(1000));
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
	
}
测试启动的main函数:

public class ExchangerMain {
	public static void main(String[] args){
		Exchanger<char[]> exchanger = new Exchanger<char[]>();
		char[] buffer1 = new char[10];
		char[] buffer2 = new char[10];
		new ProducerThread(exchanger, buffer1, 314159).start();
		new ConsumerThread(exchanger, buffer2, 265358).start();
		
	}

}
这个示例程序是通过安全的交换两个对象的这个功能实现了Producer和Consumer之间的交换。(参考Java API文档)





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值