• 线程停止与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种方法比较保守、安全。

  • 相关阅读:
    2019 牛客多校第二场 H Second Large Rectangle
    2019 牛客多校题解目录
    2019 牛客多校第一场 F Random Point in Triangle
    2019 牛客多校第一场 E ABBA
    2019 牛客多校第一场 D Parity of Tuples
    2019 牛客多校第一场 C Euclidean Distance ?
    2019 牛客多校第一场 B Integration
    2019 牛客多校第一场 A Equivalent Prefixes
    Sigils of Elohim
    UVA 1599 Ideal Path
  • 原文地址:https://www.cnblogs.com/zhrb/p/7192013.html
Copyright © 2020-2023  润新知