延伸理解: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文档)