今天闲来无事为某同学解决线程的问题,写了一个通过多线程实现Count++并输出到控制台的小程序,发现自己不小心踩了一个坑。简单来说就是其中一个线程进行Count++操作,另外一个线程将Count的数字输出到控制台。其中包含了四个类:Calculate、Print、CountManager、Main,其中Calculate类是一个线程类,对count进行加一处理;Print类也是一个线程类,对Count进行打印输出;CountManager对Calculate和Print类进行管理;Main是程序入口。
下面是各个类的代码:
public class Calculate implements Runnable {
private CountManager manager;
public Calculate(CountManager manager) {
this.manager = manager;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
// 线程睡眠,等待唤醒
synchronized (this) {
while (!manager.isCalculating()) {
this.wait();
}
}
TimeUnit.MILLISECONDS.sleep(100);
manager.setCount(manager.getCount() + 1);
// 唤醒其他线程
synchronized (manager.getPrint()) {
manager.setCalculating(false);
manager.getPrint().notifyAll();
}
}
} catch (Exception e) {
System.out.println(Thread.currentThread() + "Calculate Exiting via interrupt");
} finally {
System.out.println("Calculate Thread: " + Thread.interrupted());
System.out.println("Calculate Exiting");
}
}
}
public class CountManager {
private volatile Integer count = 0;
private volatile boolean calculating = false;
private Calculate calculate;
private Print print;
private ExecutorService exec = Executors.newCachedThreadPool();
// private volatile boolean shutdown = false;
public void start() {
calculate = new Calculate(this);
print = new Print(this);
exec.execute(calculate);
exec.execute(print);
}
public void shutdown() {
// shutdown = true;
System.out.println("Shutdown");
exec.shutdownNow();
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public boolean isCalculating() {
return calculating;
}
public void setCalculating(boolean calculating) {
this.calculating = calculating;
}
public Calculate getCalculate() {
return calculate;
}
public Print getPrint() {
return print;
}
// public boolean isShutdown() {
// return shutdown;
// }
}
public class Print implements Runnable {
private CountManager manager;
public Print(CountManager manager) {
this.manager = manager;
}
@Override
public void run() {
try {
while (!Thread.interrupted()) {
System.out.println("Print Thread: " + Thread.interrupted());
synchronized (this) {
// 线程睡眠等待计数完成
while (manager.isCalculating()) {
this.wait();
}
}
System.out.println(Thread.currentThread() + " " + new Date() + " " + manager.getCount());
// 唤醒计数线程
synchronized (manager.getCalculate()) {
manager.setCalculating(true);
manager.getCalculate().notifyAll();
}
// 睡眠2s
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
}
} catch (Exception e) {
System.out.println("Print Exiting via interrupt");
} finally {
System.out.println("Print Exiting");
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
CountManager manager = new CountManager();
manager.start();
TimeUnit.SECONDS.sleep(10);
manager.shutdown();
}
}
运行Main函数之后的结果:
根据这个运行结果,发现几个问题:
1、Main函数执行了shutdown之后,程序并没有结束
2、执行了exec.shutdownNow()之后,Print和Calculate线程的Thread.interrupted()方法的返回值还是false
首先看下Thread.interrupted()方法的注释:
Tests whether the current thread has been interrupted. The interrupted status of the thread is cleared by this method. In other words, if this method were to be called twice in succession, the second call would return false (unless the current thread were interrupted again, after the first call had cleared its interrupted status and before the second call had examined it).
注释上只是说明调用这个方法之后interrupted状态会被重置,但是上面的代码里面在线程interrupted之后也只调用了一次Thread.interrupted()方法,根据这个注释的说明并不能解释为什么Print和Calculate线程的interrupted状态一直是false。个人理解,线程被interrupted之后除了调用Thread.interrupted()方法会将状态重置,线程在waiting或者sleeping时interrupted,interrupted的状态也会被重置(目前并没有找到官方说明,后期如有发现,将补充说明)。之后的开发中,如果用Thread.interrupted()方法来结束线程,希望能有所警惕。