我是java中这种并发编程的新手,并提出了以下情况,我在这些情况下会遇到混淆。
场景1:在下面的代码中,我试图通过在GPSService类上调用 .start()来运行线程,这是一个Runnable实现。
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 successfully\n");
}
我的问题是
在并发编程中调用线程的最佳实践是什么?
当我使用第一个或第二个时,我会得到什么结果(麻烦)?
注意:我已经面临第一个方案的问题,即每隔几天该程序就会被挂起。那么,当我使用第一种方法时,该问题是否与预期有关
任何好的/有用的答案将不胜感激:)谢谢
答案 0 :(得分:3)
除了在Scenario2中管理线程终止之外,您发布的两个场景没有太大差异;您总是为每个传入请求创建一个新线程。如果你想使用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));
}
这将允许您使用线程池并控制用于服务器的线程数。如果你想使用Executor,这是首选方式。
使用服务器池,您需要确定池中有多少个线程;您有不同的选择,但您可以启动或使用固定数量或线程或尝试使用非繁忙线程的池,如果所有线程都忙,则会创建一个新线程(newCachedThreadPool()
)。要分配的线程数取决于许多因素:并发请求的数量和持续时间。服务器端代码花费的时间越多,您需要的额外线程就越多。如果您的服务器端代码非常快,那么池很可能会回收已经分配的线程(因为请求并非完全在同一时刻完成)。
我建议你(如果你还没有)在实践中阅读 Java Concurrency (任务执行是第6章);这是一本关于如何在Java中构建并发应用程序的优秀书籍。
答案 1 :(得分:1)
来自Executors
public static ExecutorService newCachedThreadPool()
创建一个根据需要创建新线程的线程池,但在它们可用时将重用以前构造的线程。这些池通常会提高执行许多短期异步任务的程序的性能。
执行调用将重用以前构造的线程(如果可用)。如果没有可用的现有线程,则将创建一个新线程并将其添加到池中。未使用60秒的线程将终止并从缓存中删除。
因此,长时间闲置的池不会消耗任何资源。请注意,可以使用ThreadPoolExecutor构造函数创建具有相似属性但不同详细信息的池(例如,超时参数)。
public static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用在共享无界队列中运行的固定数量的线程。在任何时候,最多nThreads线程将是活动的处理任务。如果在所有线程都处于活动状态时提交了其他任务,则它们将在队列中等待,直到线程可用。
如果任何线程因关闭前执行期间的故障而终止,则在需要执行后续任务时,新的线程将取代它。池中的线程将一直存在,直到明确关闭为止。
@Giovanni 表示您不必提供newCachedThreadPool
的线程数,而不是newFixedThreadPool
(),其中您必须传递线程数的最大上限在ThreadPool中。
但在这两者之间,newFixedThreadPool()
是首选。 newCachedThread Pool
可能会导致泄漏,并且由于无限制性质,您可能会达到最大可用线程数。有些人认为这是一种邪恶。
看看相关的SE问题:
Why is an ExecutorService created via newCachedThreadPool evil?