線程停止與volatile


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)的時候都能看到最近剛剛被寫入的值。

結論:

  1. 當多個線程共享可變數據的時候,每個讀或者寫數據的線程都必須執行同步(synchronized)。如果沒有同步,就無法保證一個線程所做的修改可以被另一個線程獲知。
  2. 如果需要線程之間的交互通信,而不需要互斥,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種方法比較保守、安全。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM