Java ThreadPoolExecutor并发线程池Index

一:ThreadPoolExecutor   

        从 Java 5 开始,Java 提供了自己的线程池。线程池就是一个线程的容器,每次只执行额定数量的线程java.util.concurrent.ThreadPoolExecutor 就是这样的线程池。它很灵活,但使用起来也比较复杂。 首先是构造函数。以最简单的构造函数为例:

public ThreadPoolExecutor(   

int corePoolSize,   

int maximumPoolSize,   

long keepAliveTime,   

TimeUnit unit,   

BlockingQueue<Runnable> workQueue)    

看起来挺复杂的。这里介绍一下。

corePoolSize 指的是保留的线程池大小。

maximumPoolSize 指的是线程池的最大大小。

keepAliveTime 指的是空闲线程结束的超时时间。

unit 是一个枚举,表示 keepAliveTime 的单位。

workQueue 表示存放任务的队列。

    可以从线程池的工作过程中了解这些参数的意义。线程池的工作过程如下:

1 、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

2 、当调用 execute() 方法添加一个任务时,线程池会做如下判断:

 a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

 b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。

 c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;

 d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。

3 、当一个线程完成任务时,它会从队列中取下一个任务来执行。

4 、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

    这样的过程说明,并不是先加入任务就一定会先执行。假设队列大小为 10,corePoolSize 为 3,maximumPoolSize 为 6,那么当加入 20 个任务时,执行的顺序就是这样的:首先执行任务 1、2、3,然后任务 4~13 被放入队列。这时候队列满了,任务 14、15、16 会被马上执行,而任务 17~20 则会抛出异常。最终顺序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。下面是一个线程池使用的例子:

public static void main(String[] args) {   

  BlockingQueue<Runnable> queue = new   LinkedBlockingQueue<Runnable>();   

  ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS,   queue);   

for (int i = 0; i < 20; i++) {   

executor.execute(new Runnable() {   

  public void run() {   

    try {   

         Thread.sleep(1000);   

    } catch (InterruptedException e) {   

    e.printStackTrace();   

    }   

      System.out.println(String.format("thread %d finished", this.hashCode()));   

 }   

});   

}    

executor.shutdown();   

}  

对这个例子的说明如下:

1、BlockingQueue 只是一个接口,常用的实现类有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好处在于没有大小限制。这样的话,因为队列不会满,所以 execute() 不会抛出异常,而线程池中运行的线程数也永远不会超过 corePoolSize 个,keepAliveTime 参数也就没有意义了。

2、shutdown() 方法不会阻塞。调用 shutdown() 方法之后,主线程就马上结束了,而线程池会继续运行直到所有任务执行完才会停止。如果不调用 shutdown() 方法,那么线程池会一直保持下去,以便随时添加新的任务。

   到这里对于这个线程池还只是介绍了一小部分。ThreadPoolExecutor 具有很强的可扩展性,不过扩展它的前提是要熟悉它的工作方式。 java.util.concurrent.ThreadPoolExecutor 类提供了丰富的可扩展性。你可以通过创建它的子类来自定义它的行为。例如,我希望当每个任务结束之后打印一条消息,但我又无法修改任务对象,那么我可以这样写: 除了 afterExecute 方法之外,ThreadPoolExecutor 类还有 beforeExecute() 和 terminated() 方法可以重写,分别是在任务执行之前和整个线程池停止之后执行。

   除了可以添加任务执行前后的动作之外, ThreadPoolExecutor 还允许你自定义当添加任务失败后的执行策略。你可以调用线程池的 setRejectedExecutionHandler() 方法,用自定义的 RejectedExecutionHandler 对象替换现有的策略。ThreadPoolExecutor 提供 4 个现有的策略,分别是:

ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常

ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作

ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务

ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列。

这里是一个例子:

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

除此之外,你也可以通过实现 RejectedExecutionHandler 接口来编写自己的策略。下面是一个例子:

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(String.format("Task %d rejected.", r.hashCode()));
}
}

);



 二:ThreadPoolExecutor 实现的接口 Executer

          看jdk的javadoc,ThreadPoolExecutor实现了两个接口:Executer,ExecuterService,注意, ExecuterService继承了Executer, 看看javadoc对 Executer的描述 :

public interface Executor

执行已提交的 Runnable 任务的对象。此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节、调度等)分离开来的方法。通常使用 Executor 而不是显式地创建线程。例如,可能会使用以下方法,而不是为一组任务中的每个任务调用

new Thread(new(RunnableTask())).start(): 
 Executor executor = anExecutor;
 executor.execute(new RunnableTask1());
 executor.execute(new RunnableTask2());
 
 ... 
 不过,Executor 接口并没有严格地要求执行是异步的。在最简单的情况下,执行程序可以在调用者的线程中立即运行已提交的任务: 
 class DirectExecutor implements Executor {
     public void execute(Runnable r) {
         r.run();
     }
 }

更常见的是,任务是在某个不是调用者线程的线程中执行的。以下执行程序将为每个任务生成一个新线程。 
 class ThreadPerTaskExecutor implements Executor {
     public void execute(Runnable r) {
         new Thread(r).start();
     }
 } 

        许多 Executor 实现都对调度任务的方式和时间强加了某种限制。以下执行程序使任务提交与第二个执行程序保持连续,这说明了一个复合执行程序。 
 class SerialExecutor implements Executor {
     final Queue<Runnable> tasks = new ArrayDeque<Runnable>();
     final Executor executor;
     Runnable active;
     SerialExecutor(Executor executor) {
         this.executor = executor;
     }
     public synchronized void execute(final Runnable r) {
         tasks.offer(new Runnable() {
             public void run() {
                 try {
                     r.run();
                 } finally {
                     scheduleNext();
                 }
             }
         });
         if (active == null) {
             scheduleNext();
         }
     }

     protected synchronized void scheduleNext() {
         if ((active = tasks.poll()) != null) {
             executor.execute(active);
         }
     }
 }

            此包中提供的 Executor 实现实现了 ExecutorService,这是一个使用更广泛的接口。ThreadPoolExecutor 类提供一个可扩展的线程池实现。Executors 类为这些 Executor 提供了便捷的工厂方法。



三:ThreadPoolExecutor 实现的接口 ExecutorService

public interface ExecutorService extends Executor

          ExecutorService 提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成 Future 的方法。 可以关闭 ExecutorService,这将导致其拒绝新任务。提供两个方法来关闭 ExecutorService。shutdown() 方法在终止前允许执行以前提交的任务,而 shutdownNow() 方法阻止等待任务启动并试图停止当前正在执行的任务。在终止时,执行程序没有任务在执行,也没有任务在等待执行,并且无法提交新任务。应该关闭未使用的 ExecutorService 以允许回收其资源。 
       通过创建并返回一个可用于取消执行和/或等待完成的 Future,方法 submit 扩展了基本方法 Executor.execute(java.lang.Runnable)。方法 invokeAny 和 invokeAll 是批量执行的最常用形式,它们执行任务 collection,然后等待至少一个,或全部任务完成(可使用 ExecutorCompletionService 类来编写这些方法的自定义变体)。 Executors 类提供了用于此包中所提供的执行程序服务的工厂方法。


用法示例
下面给出了一个网络服务的简单结构,这里线程池中的线程作为传入的请求。它使用了预先配置的 Executors.newFixedThreadPool(int) 工厂方法: 
 class NetworkService implements Runnable {
    private final ServerSocket serverSocket;
    private final ExecutorService pool;

    public NetworkService(int port, int poolSize)
        throws IOException {
      serverSocket = new ServerSocket(port);
      pool = Executors.newFixedThreadPool(poolSize);
    }
    public void run() { // run the service
      try {
        for (;;) {
          pool.execute(new Handler(serverSocket.accept()));
        }
      } catch (IOException ex) {
        pool.shutdown();
      }
    }
  }
  class Handler implements Runnable {
    private final Socket socket;
    Handler(Socket socket) { this.socket = socket; }
    public void run() {
      // read and service request on socket
    }
 }
 
 下列方法分两个阶段关闭 ExecutorService。第一阶段调用 shutdown 拒绝传入任务,然后调用 shutdownNow(如有必要)取消所有遗留的任务: 
 void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }
   } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }
 } 

 内存一致性效果:线程中向 ExecutorService 提交 Runnable 或 Callable 任务之前的操作 happen-before 由该任务所提取的所有操作,后者依次 happen-before 通过 Future.get() 获取的结果。

 

 

四:异步任务 Future接口

        一般情况下,调用线程池的execute方法执行的任务都是不需要返回结果的,但是在某些情况下,我们可能需要执行异步的任务,就是提交一个任务,然后我做自己的事情,不管它,一会儿我再查看这个任务返回的结果,那么这个时候,就要用到异步任务,异步任务都实现了Future接口。

        它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future 但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为底层任务的结果。 
用法示例(注意,下列各类都是构造好的。) 
 interface ArchiveSearcher { String search(String target); }
 class App {
   ExecutorService executor = ...
   ArchiveSearcher searcher = ...
   void showSearch(final String target)
       throws InterruptedException {
     Future<String> future
       = executor.submit(new Callable<String>() {
         public String call() {
             return searcher.search(target);
         }});
     displayOtherThings(); // do other things while searching
     try {
       displayText(future.get()); // use future
     } catch (ExecutionException ex) { cleanup(); return; }
   }
 }
 
 FutureTask 类是 Future 的一个实现,并且FutureTask 实现 了Runnable接口,所以可通过 Executor 来执行。例如,可用下列内容替换上面带有 submit 的构造: 
     FutureTask<String> future =
       new FutureTask<String>(new Callable<String>() {
         public String call() {
           return searcher.search(target);
       }});
     executor.execute(future);

        String result=future.get();

     System.out.println("result is:"+result);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,以下是用 JavaThreadPoolExecutor 创建线程池的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class ThreadPoolExample { public static void main(String[] args) { int corePoolSize = 2; int maximumPoolSize = 4; long keepAliveTime = 10; ExecutorService executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, java.util.concurrent.TimeUnit.SECONDS, new java.util.concurrent.LinkedBlockingQueue<Runnable>() ); // 提交任务 for (int i = 0; i < 10; i++) { executor.execute(new Task(i)); } // 关闭线程池 executor.shutdown(); } static class Task implements Runnable { private int id; public Task(int id) { this.id = id; } public void run() { System.out.println("Task " + id + " is running."); } } } ``` 在这个例子中,我们使用 ThreadPoolExecutor 创建了一个线程池。它的参数包括: - corePoolSize:线程池中核心线程的数量; - maximumPoolSize:线程池中最大线程数; - keepAliveTime:线程池中非核心线程的超时时间; - TimeUnit:超时时间的单位; - BlockingQueue:用于缓存等待执行的任务的队列。 我们还定义了一个 Task 类来模拟需要执行的任务。在主函数中,我们提交了 10 个任务给线程池,并在任务执行结束后关闭了线程池。 ### 回答2: 使用java中的ThreadPoolExecutor类可以很方便地创建线程池。首先,我们需要导入java.util.concurrent包,在代码中创建ThreadPoolExecutor对象。 ThreadPoolExecutor类的构造函数有多个参数,其中最重要的是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue和threadFactory。corePoolSize指定了线程池中保留的线程数,maximumPoolSize指定了线程池中允许的最大线程数。keepAliveTime和unit用于指定超过corePoolSize数量的空闲线程在被终止之前等待新任务的最长时间。workQueue用于存储等待执行的任务。threadFactory用于创建新线程。 下面是一个简单的示例代码: ``` import java.util.concurrent.*; public class ThreadPoolExample { public static void main(String[] args) { int corePoolSize = 5; int maximumPoolSize = 10; long keepAliveTime = 1; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10); ThreadFactory threadFactory = Executors.defaultThreadFactory(); ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory); // 添加任务到线程池 for (int i = 0; i < 20; i++) { executor.execute(new Task(i)); } // 终止线程池 executor.shutdown(); } } class Task implements Runnable { private int taskId; public Task(int taskId) { this.taskId = taskId; } @Override public void run() { System.out.println("Task " + taskId + " is running."); } } ``` 在上面的示例中,我们创建了一个ThreadPoolExecutor对象executor,它使用corePoolSize=5、maximumPoolSize=10、keepAliveTime=1秒、单位TimeUnit.SECONDS的时间单位、workQueue容量为10的ArrayBlockingQueue以及默认的线程工厂。然后,我们向线程池中添加20个任务,每个任务都会打印任务的ID。最后,我们通过executor.shutdown()方法来终止线程池。 通过使用ThreadPoolExecutor类,我们可以方便地创建和管理线程池,从而实现任务的并发执行。 ### 回答3: Java中的ThreadPoolExecutor是一个用于创建和管理线程池的类。 首先,我们需要导入java.util.concurrent包中的ThreadPoolExecutor类。接下来,我们可以使用ThreadPoolExecutor的构造函数来创建一个线程池对象。构造函数接受一些参数,如核心线程数、最大线程数、线程等待时间等。 例如,我们可以使用以下代码创建一个线程池对象: ``` ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); ``` 在上面的代码中,我们创建了一个核心线程数为5,最大线程数为10的线程池。执行的任务将被放入一个无界队列LinkedBlockingQueue中,并且线程空闲时等待的时间为60秒。 接下来,我们可以通过调用execute()方法来提交任务给线程池执行。例如,我们可以创建一个实现了Runnable接口的任务,并将其提交给线程池执行,如下所示: ``` Runnable task = new MyTask(); executor.execute(task); ``` 上述代码中,MyTask是一个实现了Runnable接口的自定义任务类。我们创建了一个MyTask对象,并使用execute()方法将其提交给线程池执行。 最后,当不再需要使用线程池时,我们可以调用shutdown()方法来关闭线程池。例如: ``` executor.shutdown(); ``` 在上述代码中,executor.shutdown()方法将使线程池停止接受新的任务,并等待所有已提交的任务执行完成后,关闭线程池。 通过使用ThreadPoolExecutor类,我们可以方便地创建和管理线程池,提高多线程程序的性能和效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值