如何停止线程?
A. 用volatile的boolean作为标记来停止 B. 用stop()方法让线程停止 C. 用interrupt来请求线程停止
解答: 应该选C。
原理:用interrupt来请求线程停止而不是强制,好处是安全。 想停止线程,要请求方、被停止方、子方法被调用方相互配合才行: a) 作为被停止方 :每次循环中或者适时检查中断信号,并且在可能抛出InterrupedException的地方处理该中断信号; b) 请求方 :发出中断信号; c) 子方法调用方(被线程调用的方法的作者)要注意 :优先在方法层面抛出InterrupedException,或者检查到中断信号时,再次设置中断状态; 最后再说错误的方法:stop/suspend已废弃,volatile的boolean无法处理长时间阻塞的情况 ?
错误停止线程之volatile设置boolean标记位
从三步来展开: 1、看似可行(部分情况适用) 2、错误之处 3、修正方案
一、看似可行
package threadcoreknowledge.stopthreads.volatiledemo;
public class WrongWayVolatile implements Runnable{
public volatile boolean canceled = false;
@Override
public void run() {
int num = 0;
try {
while(num <= 10000 && !canceled) {
if (num % 100 == 0) {
System.out.println(num + "是100的倍数");
}
num++;
Thread.sleep(1);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
WrongWayVolatile r = new WrongWayVolatile();
Thread thread = new Thread(r);
thread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
r.canceled = true;
}
}
二、错误之处
详述示例代码整体思路:
我们首先有一个生产者Producer 和一个消费者Consumer ,生产者负责生产(代码是循环num++,把是100的倍数加入到一个阻塞队列BlockingQueue),BlockingQueue的容量初始化设置为10,当队列满了之后(就是等于10),生产者会阻塞,也就是Producer的run()方法的while循环中的storage.put(num)会进入wait状态阻塞 ,这时必须要等消费者Consumer拿num数据,就是storage.take(num),生产者才能继续往阻塞队列BlockingQueue加num,也就是storage.put(num)这步才会执行走下去。然后就是我们设置生产者Producer的生产速度很快,消费者Consumer消费速度慢Thread.sleep(100); ,目的就是为了让生产者Producer大多数情况处于一个阻塞状态 ,最后就是设置一个needMoreNums()方法,方法目的也很简单,就是让消费者在某一时刻说:我不需要更多数据了,就是消费者Consumer不需要数据了,那对应的,这时生产者Producer也就应该停止生产了,也就是我们代码编写者此时应该请求中断生产者Producer的线程,然后我们就通过volatile设置boolean标记位 的方式中断线程,看看会发生什么。。。
package threadcoreknowledge.stopthreads.volatiledemo;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue storage = new ArrayBlockingQueue<>(10);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()){
System.out.println(consumer.storage.take()+"被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了");
producer.canceled = true;
System.out.println(producer.canceled);
}
}
class Producer implements Runnable{
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage){
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while(num <= 100000 && !canceled) {
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中了。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("生产者停止运行");
}
}
}
class Consumer{
BlockingQueue storage;
public Consumer(BlockingQueue storage){
this.storage = storage;
}
public boolean needMoreNums(){
if (Math.random() > 0.95){
return false;
}
return true;
}
}
打印结果 这说明了已经把volatile设置的标记位canceled 设置为true ,但神奇地是Producer的线程并没有被停止,而导致线程没有被停止原因是: 三、修正方案 1、改用interrupt()中断 2、把Producer 和 Consumer改成内部类 (没什么原因,就是感受一下内部类用法)
下面直接贴代码:
package threadcoreknowledge.stopthreads.volatiledemo;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class WrongWayVolatileFixed {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
ArrayBlockingQueue storage = new ArrayBlockingQueue<>(10);
Producer producer = body.new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = body.new Consumer(storage);
while (consumer.needMoreNums()){
System.out.println(consumer.storage.take()+"被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了");
producerThread.interrupt();
}
class Producer implements Runnable{
public volatile boolean canceled = false;
BlockingQueue storage;
public Producer(BlockingQueue storage){
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while(num <= 100000 && !Thread.currentThread().isInterrupted()) {
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放到仓库中了。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println("生产者停止运行");
}
}
}
class Consumer{
BlockingQueue storage;
public Consumer(BlockingQueue storage){
this.storage = storage;
}
public boolean needMoreNums(){
if (Math.random() > 0.95){
return false;
}
return true;
}
}
}
关于!Thread.currentThread().isInterrupted() 是否多余的问题: 
?
无法响应中断时如何停止线程?(处理不可中断 的阻塞)
A. 用interrupt方法来请求停止线程 B. 不可中断的阻塞无法处理 C. 根据不同的类调用不同的方法
解答: 应该选C。 如果线程阻塞是由于调用了 wait(),sleep() 或 join() 方法,你可以中断线程,通过抛出 InterruptedException 异常来唤醒该线程。 但是对于不能响应InterruptedException的阻塞,很遗憾,并没有一个通用的解决方案。 但是我们可以利用特定的其它的可以响应中断的方法 ,比如ReentrantLock.lockInterruptibly(),比如关闭套接字使线程立即返回等方法来达到目的。 答案有很多种,因为有很多原因会造成线程阻塞,所以针对不同情况,唤起的方法也不同。
总结就是说如果不支持响应中断,就要用特定方法来唤起,没有万能药。
|