線程中止與volatile

1.使用標誌位中止線程

在Java中但願中止線程,可使用設置標誌位的方法,以下例所示: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)方法都要同步。ide

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

結論:線程

  1. 當多個線程共享可變數據的時候,每一個讀或者寫數據的線程都必須執行同步(synchronized)。若是沒有同步,就沒法保證一個線程所作的修改能夠被另外一個線程獲知。
  2. 若是須要線程之間的交互通訊,而不須要互斥,volatile修飾符就是一種能夠接收的同步形式。

參考:code

Effective Javaget

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種方法比較保守、安全。

相關文章
相關標籤/搜索