如何在退出前安全关闭所有线程

时间:2018-09-20 14:52:08

标签: java

我有一个启动几个线程的应用程序,最终一个线程可能需要退出整个应用程序,但是其他线程可能处于任务中间,因此我想让它们在退出之前继续其当前循环。

在下面的示例中,Thread2不知道何时Thread1试图退出,它只是强制所有操作立即完成。

如何让Thread2、3和4等在关闭之前完成它们的循环?

编辑::要解决重复的问题:与典型情况不同,父类不能负责伪造关闭,任何单独的线程都必须能够发起关闭请求。关闭。

Edit2 :我还留下了一个答案,最后是我做的是接受答案的实现。

class Scratch {
    public static void main(String[] args) {
        Thread Task1 = new Thread(new Task1());
        Task1.start();

        Thread Task2 = new Thread(new Task2());
        Task2.start();

        // ... more threads
    }

    public class Task1 implements Runnable {
        public void run() {
            while (true) {
                // ...
                System.exit(0);
                // ...
            }
        }
    }

    public class Task2 implements Runnable {
        public void run() {
            while (true) {
                // ...
                // I need to know about System.exit(0) to exit my loop
                // ...
            }
        }
    }
}

6 个答案:

答案 0 :(得分:3)

您可以使用所有线程将不断检查的volatile boolean变量。如果一个线程将该变量的值设置为false,则所有线程都将看到新值并离开while循环。

说明volatile变量中的读/写操作是原子的。除此之外,volatile变量的值不会被缓存,因此所有线程都看到相同的值。

class Scratch {

    private static volatile boolean isRunning = true;

    public static void main(String[] args) {
        Thread Task1 = new Thread(new Task1());
        Task1.start();

        Thread Task2 = new Thread(new Task2());
        Task2.start();

        // ... more threads
    }

    public class Task1 implements Runnable {
        public void run() {
            while (isRunning) {
                // ...
                isRunning = false; // let's stop all threads
                // ...
            }
        }
    }

    public class Task2 implements Runnable {
        public void run() {
            while (isRunning) {
                // ...
                // I need to know about System.exit(0) to exit my loop
                // ...
            }
        }
    }
}

答案 1 :(得分:1)

您自己创建线程被认为是“不良做法”,您应该考虑使用ExecutorService。同步应该通过中断线程来完成。

class Scratch {
    private final ExecutorService executorService;
    public Scratch(ExecutorService es) {
       this.executorService = es;
    }
    /** Convience constructor */
    public Scratch() {this(Executors.newCachedThreadPool());}

    public class Task1 implements Callable<Void> {
        public Void call() throws Exception {
            while(true) {
                ...
                executorService.shutdownNow(); // interrupt all running threads
                                               // (including Task1) started by
                                               // given executor service
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
            return null;
        }
     }

     public class Task2 implements Callable<Void> {
         public Void call() throws Exception {
             while(true) {
                 // check if the thread was interrupted, if so throw Exception
                 if (Thread.interrupted())
                     throw new InterruptedException();
             }
             return null;
         }
     }

     public static void main(String ... args) {
         Scratch s = new Scratch();
         s.executorService.submit(new Task1());
         s.executorService.submit(new Task2());
     }
 }

答案 2 :(得分:1)

除了通过使用 volatile布尔变量实现的方法(非常有用)之外,您还可以考虑使用 listeners 。它们通常在整个Android API中都使用。

首先,创建定义侦听器的接口。这是一个示例:

public interface TaskListener {
    public void onFinish();
}

现在,在要通知您任务完成的类上实现此接口。看下面:

public class Task2 implements Runnable, TaskListener { 
    public void run() { 
        while (!Thread.currentThread().isInterrupted()) { 
            //... 
        } 
    }

    public void onFinish() {
         //perform your exit operations here.
         Thread.currentThread().interrupt();
    } 
}

现在,准备您的主要任务课程,以接收侦听器。看看:

public class Task1 implements Runnable { 
    private ArrayList<TaskListener> listeners = new ArrayList<>();

    public void run() { 
        while (true) { 
            // ... 
            this.finish();
            // ... 
        } 
    } 

    public void addListener (TaskListener listener) {
        this.listeners.add(listener);
    } 

    private void finish() {
        for(TaskListener listener: this.listeners) {
             listener.onFinish();
        } 
    } 
 }

在完成所有设置后,现在很容易听完任务的完成情况。看下面:

public static void main(String[] args) { 
    Thread task1 = new Thread(new Task1()); 
    Thread task2 = new Thread(new Task2()); 

    task1.addListener(task2);
    task1.start();
    task2.start();
} 

完成!现在,每次调用task1.finish()时,它的所有侦听器(在此示例中仅为task2)都被通知(它们的onFinish方法被调用)。

注意:如果您不想在所有任务中实施TaskListener。这是使用 lambda表达式的示例:

public static void main(String[] args) { 
    Thread task1 = new Thread(new Task1()); 
    Thread task2 = new Thread(new Task2()); 

    task1.addListener(() -> {
        //this entire block will be called when task1 finishes
        task2.interrupt();
    });

    task1.start();
    task2.start();
} 

注意2::我在手机上写下了这个答案。尽管我多次修改了代码,看起来还可以,但是直到我回到家,我才能对其进行测试。

答案 3 :(得分:0)

您不能轻易做到这一点。

首先System.exit(0)退出JVM,并且它不关心线程。

您需要做什么:

  • 定义您的 own 框架来管理线程
  • 还会为您的线程派生一个“体系结构”。任何应该由您的框架控制的内容都将需要A)查询“您将运行多长时间?”的方法。和B)“准备下山”

当然:在现实世界中,您的应用程序中可能有很多线程运行着您甚至都不知道的线程(例如,当您在某处使用someCollection.parallelStream()时:一个JVM范围内的线程池,其中的线程不在您的控制范围之内。

某些中间立场可能是“线程管理器”组件。不想被杀死的线程必须向该管理器注册。

但是仍然:即使这样很难。例如,当某些线程陷入僵局时会发生什么?还是等待JVM“外部”发生某些事情?要弄清线程是否还在真正地进展中,还需要花费大量的精力。

答案 4 :(得分:0)

按照@HugoTeixeira的建议,我最终使用了易失性和原子整数来跟踪线程。我还制作了一个控制线程来定期检查这些值并确定是否该退出。

class Scratch {
    public static volatile boolean isRunning = true;
    public static AtomicInteger threadCount = new AtomicInteger();

    public static void main(String[] args) {
        Thread controlThread = new Thread(new ControlThread());
        controlThread.start();

        Thread Task1 = new Thread(new Task1());
        Task1.start();
        if(Task1.isAlive()) { threadCount.incrementAndGet(); }

        Thread Task2 = new Thread(new Task2());
        Task2.start();
        if(Task2.isAlive()) { threadCount.incrementAndGet(); }

        // ... more threads
    }
}

任务线程:

public class Task1 implements Runnable {
    public void run() {
        while (isRunning) {
            // ...
            isRunning = false; // let's stop all threads
            // ...
        }
     }
  }

控制线程:

import static Scratch.isRunning;
import static Scratch.threadCount;

public class ControlThread implements Runnable {
    public void run() {
        while(true) {
            try {
                // Check if we have stopped running the app AND all threads have finished up.
                if(!isRunning && threadCount.get() == 0) {
                    // Safely close the app
                    System.exit(0);
                }
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                // Ignore exceptions
            }
        }
    }
}

答案 5 :(得分:0)

如果打算等待所有Thread完成执行。考虑使用ExecutorServiceFuture。下面的草图代表了实现它的一般想法。

final ExecutorService pool = Executors.newCachedThreadPool();

Future<?> task1 = pool.submit(() -> {
    //do loop task1
});

Future<?> task2 = pool.submit(() -> {
    //do loop task2
});

while(!task1.isDone() && !task2.isDone()){
   //waits to complete
}

所有任务完成后,使用pool.shutdown()关闭执行程序。然而,这只是简单的例子。有关更多详细信息,请参见ExecutorService javadoc。

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html