一个常用但但并不一定能中断线程方法(因为如果此线程阻塞,则前面的判断可能会永远执行不到)
package seven; import java.math.BigInteger; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import common.Utils; public class PrimeProducer extends Thread { final BlockingQueue<BigInteger> queue; private volatile boolean cancelled = false; public PrimeProducer(BlockingQueue queue) { this.queue = queue; } @Override public void run() { BigInteger p = BigInteger.ONE; /** * 分析下面循环的执行过程 当线程执行时,检测当前线程的cancelled标志,如果没有被设置true,那么执行 queue.put方法 * 此方法有可能导入线程阻塞,如果我们上面循环条件用 !cancelled进行判断的话,当消费者 * 要求阻塞生产者线程时,会判断canceled状态,而当前生产者处于阻塞状态,无法无法进行 * cancelled值的比较,而消费者可能不再消费,此时queue一直处于满的状态,会一直阻塞, 所以当前的停止线程并不适用 */ while (!cancelled) { try { queue.put(p = p.nextProbablePrime()); System.out.println(Thread.currentThread().getName() + " 生产数字 " + p); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + " 线程中断"); } } } public void cancel() { this.cancelled = true; } public static void main(String args[]) { BlockingQueue<BigInteger> queue = new ArrayBlockingQueue<BigInteger>(3); PrimeProducer producer = new PrimeProducer(queue); producer.start(); for (;;) { try { System.out.println(Thread.currentThread().getName() + "消费数据 " + queue.take()); // 从队列取出一个数 Utils.sleep(1); // 停止1s,显示出消费速度慢于生产速度 producer.cancel(); // 消费者请求停止生产 break; // 消费者停止消费 } catch (InterruptedException e) { System.out.println("被中断了"); } } } }
线程将不会停止,而是一直阻塞到这个地方
下面是一个改进的例子,用中断来进行线程的停止,因为,在线程阻塞状态下,中断会引起线程抛出一个 InterruptedException 而且设置中断标志位,这样,在线程阻塞的时候
我们也可以照样将线程停止;只是有一点要注意,在线程阻塞处理中断时,会抛出一个异常,而且设置中断标志位,线程由waitingQueue 进入到 Runnable Queue,
等待JVM的再次调度,再次调试时,就是从 catch捕获的地方开始执行了
为什么要将 while 写在了循环之中,而不是写在 try 之外?
当线程中 put 处于阻塞状态时,我们不能利用前面的那个例子进行线程停止,我们是利用的线程中断,主线程向 这个线程发送了
一个中断请求,而这个线程在阻塞状态收到一个中断请求会产生一个InterruptedException,然后将 中断标记置为 false(也就是清空
中断标记),如果线程不处于阻塞状态收到了一个中断请求,那么线程的中断标志位会被置为 trur,也就是这一点,让我们把循环写在了 catch 中的,如果线程不处于阻塞
,收到一个抗洪请求后,中断标志为 true,这样循环的条件就不满足,线程会停止,而线程处于阻塞状态时,收到一个抗洪请求时,抗洪标记会被清除,那么
如果循环在 catch 外的话,执行完catch 还会在执行循环,因此,只能利用catch跳出循环,而结束线程
package seven; import java.math.BigInteger; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import common.Utils; public class PrimeProducerStop extends Thread { final BlockingQueue<BigInteger> queue; private volatile boolean cancelled = false; public PrimeProducerStop(BlockingQueue queue) { this.queue = queue; } /** * 解析下面的这段代码: * 为什么要将 while 写在了循环之中,而不是写在 try 之外? 当线程中 put 处于阻塞状态时,我们不能利用前面的那个例子进行线程停止,我们是利用的线程中断,主线程向 这个线程发送了 一个中断请求,而这个线程在阻塞状态收到一个中断请求会产生一个InterruptedException,然后将 中断标记置为 false(也就是清空 中断标记),如果线程不处于阻塞状态收到了一个中断请求,那么线程的中断标志位会被置为 trur,也就是这一点,让我们把循环写在了 catch 中的,如果线程不处于阻塞 ,收到一个抗洪请求后,中断标志为 true,这样循环的条件就不满足,线程会停止,而线程处于阻塞状态时,收到一个抗洪请求时,抗洪标记会被清除,那么 如果循环在 catch 外的话,执行完catch 还会在执行循环,因此,只能利用catch跳出循环,而结束线程 */ @Override public void run() { BigInteger p = BigInteger.ONE; try { while (!Thread.currentThread().isInterrupted()) { //为什么要将循环写在 try{}之中,而不是之外? queue.put(p = p.nextProbablePrime()); System.out.println(Thread.currentThread().getName() + " 生产数字 " + p); } } catch (InterruptedException e) { //让catch 直接跳出循环,这样就起到了停止线程的作用 System.out.println(Thread.currentThread().getName() + " 线程中断"); System.out.println(Thread.currentThread().isInterrupted()); } System.out.println(Thread.currentThread().getName() + " is over"); } public void cancel() { interrupt(); } public static void main(String args[]) { BlockingQueue<BigInteger> queue = new ArrayBlockingQueue<BigInteger>(3); PrimeProducerStop producer = new PrimeProducerStop(queue); producer.start(); for (;;) { try { System.out.println(Thread.currentThread().getName() + "消费数据 " + queue.take()); // 从队列取出一个数 Utils.sleep(1); // 停止1s,显示出消费速度慢于生产速度 producer.cancel(); // 消费者请求停止生产 break; // 消费者停止消费 } catch (InterruptedException e) { System.out.println("被中断了"); } } } }