Java线程池示例

时间:2020-02-23 14:37:16  来源:igfitidea点击:

活动线程消耗系统资源,这可能导致JVM创建太多线程,这意味着系统将很快耗尽内存。

这就是Java中线程池帮助解决的问题。

线程池是如何工作的?

线程池为当前任务重用以前创建的线程。这解决了需要太多线程的问题,因此内存不足不是一个选择。你甚至可以把线程池看作是一个回收系统。它不仅消除了内存不足的选项,还使应用程序响应非常快,因为在请求到达时已经存在一个线程。

上图的工作流允许我们控制应用程序正在创建的线程数,还允许我们控制调度任务的执行,并将传入的任务保留在队列中。

执行者、执行者、执行者服务

Java提供了Executor框架,这意味着我们只需要实现可运行的对象并将它们发送给Executor来执行。

要使用线程池,首先我们需要创建ExecutorService对象并将任务传递给它。ThreadPoolExecutor类设置核心池大小和最大池大小。然后按顺序执行runnables。

不同的执行器线程池方法

newFixedThreadPool(int size) - creates a fixed size thread pool

newCachedThreadPool() - creates a thread pool that creates new threads if needed but will also use previous threads if they are available

newSingleThreadExecutor() - creates a single thread

ExecutorService接口包含许多用于控制任务进度和管理服务终止的方法。我们可以使用未来实例来控制任务的执行。如何使用Future的示例:

ExecutorService execService = Executors.newFixedThreadPool(6);
Future<String> future = execService.submit(() -> "Example");
String result = future.get();

ThreadPoolExecutor允许我们实现一个可扩展的线程池,该线程池有许多参数,这些参数是*corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、handler、threadFactor。*但是,corePoolSize、maximumPoolSize和keepAliveTime是主要参数,因为它们在每个构造函数中都使用。

corePoolSize是池中要保留的线程数,即使它们处于空闲状态,除非设置了allowCoreThreadTimeOut。

maximumPoolSize是池中允许的最大线程数。

keepAliveTime是当线程数大于核心时,这是多余的空闲线程在终止前等待新任务的最长时间。

有关其他参数的详细信息,请访问原始的Oracle文档

线程池实现示例

工作流程步骤:

创建要执行的任务

使用执行器创建执行器池

将任务传递到执行器池

关闭执行器池

任务.java

import java.text.SimpleDateFormat;  
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

//(Step 1) 
public class Task implements Runnable    { 
  private String name; 

  public Task(String name) { 
      this.name = name; 
  } 

  public void run() { 
      try {
          for (int i = 0; i < 5; i++) { 
              if (i == 1) { 
                  Date date = new Date(); 
                  SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
                  System.out.println("Time initialization for task " + this.name + " is " + ft.format(date));    
              } 
              else { 
                  Date date = new Date(); 
                  SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
                  System.out.println("Execution time for task " + this.name + " is " + ft.format(date));    
              } 
              Thread.sleep(1000); 
          } 
      } 
      catch(InterruptedException error) { 
          error.printStackTrace(); 
      } 
      System.out.println(this.name + " completed"); 
  } 
}

主类

import java.text.SimpleDateFormat;  
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 

public class Main { 
  public static void main(String[] args) { 
      Runnable task1 = new Task("task 1"); 
      Runnable task2 = new Task("task 2"); 
      Runnable task3 = new Task("task 3"); 
      Runnable task4 = new Task("task 4"); 
      Runnable task5 = new Task("task 5");       

      //(Step 2) 
      ExecutorService pool = Executors.newFixedThreadPool(3);   

      //(Step 3) 
      pool.execute(task1); 
      pool.execute(task2); 
      pool.execute(task3); 
      pool.execute(task4); 
      pool.execute(task5);  

      //(Step 4) 
      pool.shutdown();     
  } 
}

输出

Time initialization for task task 2 is 10:18:40
Time initialization for task task 1 is 10:18:40
Time initialization for task task 3 is 10:18:40
Execution time for task task 3 is 10:18:41
Execution time for task task 1 is 10:18:41
Execution time for task task 2 is 10:18:41
Execution time for task task 2 is 10:18:42
Execution time for task task 3 is 10:18:42
Execution time for task task 1 is 10:18:42
Execution time for task task 1 is 10:18:43
Execution time for task task 3 is 10:18:43
Execution time for task task 2 is 10:18:43
Execution time for task task 3 is 10:18:44
Execution time for task task 1 is 10:18:44
Execution time for task task 2 is 10:18:44
task 2 completed
task 1 completed
task 3 completed
Time initialization for task task 4 is 10:18:45
Time initialization for task task 5 is 10:18:45
Execution time for task task 4 is 10:18:46
Execution time for task task 5 is 10:18:46
Execution time for task task 4 is 10:18:47
Execution time for task task 5 is 10:18:47
Execution time for task task 5 is 10:18:48
Execution time for task task 4 is 10:18:48
Execution time for task task 4 is 10:18:49
Execution time for task task 5 is 10:18:49
task 4 completed
task 5 completed

以上代码实现分解:

任务.java表示任务类。每个任务都有一个name实例变量,并且每个任务都是通过使用构造函数实例化的。这个类有一个方法,叫做run。在run方法体中,有一个for循环,该循环按任务数迭代。在我们的例子中,有5个任务,这意味着它将运行5次。第一次迭代,打印当前任务初始化的时间。其他迭代,打印执行时间。打印后,有一个线程睡眠()方法调用,用于以1秒延迟显示每个迭代消息。 注意方法名“run”的调用很重要,因为它是一个抽象方法,来自于我们的任务类正在实现的Runnable

任务4和任务5只在池中的踏板空闲时执行。在此之前,另外的任务被放入队列中

执行完所有任务后,关闭线程池。

线程池什么时候有用

组织服务器应用程序时。正如本文开头所述,在组织服务器应用程序时,使用线程池非常有效,因为如果有许多任务,它会自动将它们放入队列中。不仅如此,它还可以防止内存耗尽,或者至少可以显著地减缓内存耗尽的过程。使用ExecutorService可以更容易地实现。