1.使用標志位停止線程
在Java中希望停止線程,可以使用設置標志位的方法,如下例所示:
class SimpleTask implements Runnable{
private boolean stop = false;
public void stop(){
stop = true;
}
@Override
public void run() {
while(!stop){
}
System.out.println("quit");
}
}
public class StopThreadTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
SimpleTask simpleTask = new SimpleTask();
executor.execute(simpleTask);
executor.shutdown();
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String word = sc.next();
if(word.equals("stop")){
System.out.println("stop the task");
simpleTask.stop();
}else if(word.equals("!"))
break;
}
}
}
然而無法成功停止線程。原因,沒有同步,就不能保證后台線程何時“看到”main線程堆stop的值所做的改編。虛擬機將
while(!stop){}
//轉化為
if(!stop)
while(true){}
改進,使用同步方法訪問stop域。注意:讀(getStop)寫(stop)方法都要同步。
class SimpleTask implements Runnable{
private boolean stop = false;
public synchronized void stop(){
stop = true;
}
public synchronized boolean getStop(){
return stop;
}
@Override
public void run() {
while(!getStop()){
}
System.out.println("quit");
}
}
public class StopThreadTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
SimpleTask simpleTask = new SimpleTask();
executor.execute(simpleTask);
executor.shutdown();
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String word = sc.next();
if(word.equals("stop")){
System.out.println("stop the task");
simpleTask.stop();
}else if(word.equals("!"))
break;
}
}
}
使用volatile關鍵字可以獲得一個更簡潔、性能更好的版本
class SimpleTask implements Runnable{
private volatile boolean stop = false;
public void stop(){
stop = true;
}
@Override
public void run() {
while(!stop){
}
System.out.println("quit");
}
}
public class StopThreadTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newSingleThreadExecutor();
SimpleTask simpleTask = new SimpleTask();
executor.execute(simpleTask);
executor.shutdown();
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
String word = sc.next();
if(word.equals("stop")){
System.out.println("stop the task");
simpleTask.stop();
}else if(word.equals("!"))
break;
}
}
}
原因:雖然volatile不執行互斥訪問,但它可以保證任何一個線程(比如本例中的main線程)讀取該域(stop)的時候都能看到最近剛剛被寫入的值。
結論:
- 當多個線程共享可變數據的時候,每個讀或者寫數據的線程都必須執行同步(
synchronized)。如果沒有同步,就無法保證一個線程所做的修改可以被另一個線程獲知。 - 如果需要線程之間的交互通信,而不需要互斥,
volatile修飾符就是一種可以接收的同步形式。
參考:
Effective Java
2.使用線程的interrupt方法停止線程
原始鏈接:How can I kill a thread? without using stop();
public class HelloWorld {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Runnable() {
public void run() {
try {
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(5000);
System.out.println("Hello World!");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
thread.start();
System.out.println("press enter to quit");
System.in.read();
thread.interrupt();
}
}
使用這種方法停止線程的好處:Interrupting 可以讓sleep()與wait()的線程直接被拋出異常,然后被終止。而不用等待其sleep完才能終止。
但也有不少人對這種方法提出質疑,認為這樣終止線程比較危險。
總的來說使用第1種方法比較保守、安全。
