在Java中并发运行线程时,ExecutorService的execute和thread.run有什么区别?



我是Java中并发编程的新手,并提出了以下场景,在这些场景中,我感到困惑何时使用哪个。

场景 1:在下面的代码中,我试图通过在GPSService类上调用.start((来运行线程,这是一个可运行的实现。

int clientNumber = 0;
ServerSocket listener = new ServerSocket(port);
while (true) {
            new GPSService(listener.accept(), clientNumber++, serverUrl).start();
} 

场景 2:在下面的代码中,我尝试使用 ExecutorService 类运行线程,如下所示

int clientNumber = 0;
ServerSocket listener = new ServerSocket(port);
while(true) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(new GPSService(listener.accept(), client++, serverUrl));
        executor.shutdown();
        while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
            // Threads are still running
            System.out.println("Thread is still running");
        }
        // All threads are completed
        System.out.println("nThread completed it's execution and terminated successfullyn");              
}

我的问题是
在并发编程中调用线程的最佳实践是什么?
当我使用第一个或第二个时,我最终会得到什么结果(麻烦(?
注意:我一直面临着第一种情况的问题,即程序每隔几天就会挂起。那么,当我使用第一种方法时,这个问题是否相关/预期。?
任何好的/有用的答案将不胜感激:)谢谢

您发布的两个方案没有太大区别,除了在方案 2 中管理线程终止; 您始终为每个传入的请求创建一个新线程。如果你想使用ThreadPool,我的建议不是为每个请求创建一个,而是为每个服务器创建一个并重用线程。像这样:

public class YourClass {
//in init method or constructor 
ExecutorService executor = Executors....;// choose from newCachedThreadPool() or newFixedThreadPool(int nThreads) or some custom option

int clientNumber = 0;
ServerSocket listener = new ServerSocket(port);
while(true) {
    executor.execute(new GPSService(listener.accept(), client++, serverUrl));
}

这将允许您使用线程池并控制要用于服务器的线程数。如果您想使用遗嘱执行器,这是首选方法。

对于服务器池,您需要决定池中有多少线程;您有不同的选择,但您可以启动或具有固定数量的线程或尝试使用非繁忙线程的池,如果所有线程都繁忙,它会创建一个新线程(newCachedThreadPool()(。要分配的线程数取决于许多因素:并发请求数及其持续时间。服务器端代码花费的时间越多,对额外线程的需求就越多。如果您的服务器端代码非常快,则池很有可能可以回收已分配的线程(因为请求不会在同一时刻全部出现(。

例如,假设您在一秒钟内有 10 个请求,每个请求持续 0.2 秒;如果请求到达秒的 0、0.1、0.2、0.3、0.4、0.5、..part(例如 23/06/2015 7:16:00:00、23/06/2015 7:16:00:01、23/06/2015 7:16:00:02(您只需要三个线程,因为到达 0.3 的请求可以由处理第一个请求的线程(位于 0 的线程(执行, 依此类推(时间 0.4 的请求可以重用用于 0.1 处请求的线程(。由三个线程管理的十个请求。

我建议你(如果你还没有的话(阅读Java并发实践(任务执行是第6章(;这是一本关于如何在Java中构建并发应用程序的好书。

来自 oracle 文档 来自 Executors

public static ExecutorService newCachedThreadPool()
创建一个线程池

,该线程池根据需要创建新线程,但在以前构造的线程可用时将重用它们。这些池通常会提高执行许多短期异步任务的程序的性能。

要执行的调用将重用以前构造的线程(如果可用(。如果没有现有线程可用,则将创建一个新线程并将其添加到池中。六十秒未使用的线程将终止并从缓存中删除。

因此,保持空闲足够长时间的池不会消耗任何资源。请注意,可以使用 ThreadPoolExecutor 构造函数创建具有相似属性但不同详细信息(例如超时参数(的池。

public static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池

,该线程池重用在共享的无限队列中运行的固定数量的线程。在任何时候,大多数 nThreads 线程都将是活动的处理任务。如果在所有线程都处于活动状态时提交其他任务,它们将在队列中等待,直到线程可用。

如果任何线程在关闭之前由于执行期间失败而终止,如果需要执行后续任务,将有一个新线程来取代它。池中的线程将一直存在,直到显式关闭。

@Giovanni 是说您不必提供线程数newCachedThreadPool不像 newFixedThreadPool ((,您必须传递 ThreadPool 中线程数的最大上限。

但在这两者之间,newFixedThreadPool()是首选。 newCachedThread Pool可能会导致泄漏,并且由于无限的性质,您可能会达到最大可用线程数。有些人认为这是一种邪恶。

看看相关的SE问题:

为什么通过newCachedThreadPool邪恶创建ExecutorService?

最新更新