一个常用但但并不一定能中断线程方法(因为如果此线程阻塞,则前面的判断可能会永远执行不到)
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("被中断了");
}
}
}
}