java线程池以及newCachedThreadPool使用过程中的问题

      为什么要用线程池?原因很简单,性能好,而且不用自己费心费力的管理线程

      1、线程池基本说明及定义

      从JDK 1.5开始,添加了Executors工具类,这个类定义了Executor、ExecutorService、ScheduledExecutorService、ThreadFactory、Calable类的工厂和实用方法。在Executors类的源码注释中,这样写道:

      该类包含:

      1. 提供能够创建并返回设置了常用配置的ExecutorService的方法
      2. 提供能够创建并返回设置了常用配置的ScheduledExecutorService的方法
      3. 提供能够创建并返回一个经过包装(禁用了特定的实现方法,仅仅暴露ExecutorService的实现)的ExecutorService方法
      4. 提供能够创建并返回能修改新建线程状态的ThreadFactory的方法
      5. 提供能够创建并返回非闭包形式的Callable,因此能够在需要Callable的执行方法中使用
      Executors类提供4种线程池:

      1. newFixedThreadPool:创建固定大小的线程池

 public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
 }
 public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
 }
      相比下面将要介绍的newCachedThreadPool,newFixedThreadPool 可控制线程最大并发数,当线程池中的线程数达到其设定大小时,其余新创建的线程会在LinkedBlockingQueue队列中等待。当线程池中的某个线程失败而终止时,新的线程会代替它执行剩下的任务。线程池中的线程只有在显式调用shutdown函数时才会退出线程池

      2. newCachedThreadPool:创建 可缓存线程池,当线程池中的线程空闲时间超过60s,便会终止该空闲线程并从缓存线程池中移除
 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
 }

      该线程池可根据需要,创建新线程,从上面代码中可以看出,其线程数最大可为Integer.MAX_VALUE,如果控制不好,使用该线程池可能导致线程创建过多,反而影响性能。因此,可以注意一下该线程池的使用场景:对于大量短暂异步任务的程序来说,使用该线程池能够大大提高性能 

      3. newSingleThreadExecutor,创建单线程的线程池     

 public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
 }
 public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
 }
      当该单线程在shutdown之前由于失败而终止时,将会有新的线程来代替它执行剩下任务。加入到该线程池里的线程会按顺序执行,一个时刻保证 只有一个线程在运行

      4. newScheduledThreadPool,创建 固定大小且能够执行 定时或周期性任务的线程池  
 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
 }
 public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
 }

      2、线程池使用    

      使用过程很简单,下面以newFixedThreadPool为例:创建一个大小为3的一个线程池,一共10个线程任务,每个任务会输出当前执行线程的名称,然后睡眠500ms(睡眠的目的是使结果更清晰)
public class TestNewFixedThreadPool {
    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; ++i) {
            newFixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

      结果:

pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-3

      从输出结果可以看出,在线程池中一共有3个线程,原因是该线程池控制了最大并发线程数为3。当使用newCachedThreadPool时,由于存在睡眠时长,很有可能会有10个线程来执行

      3、newCachedThreadPool使用过程中遇到的问题

      在写代码的时候,遇到一个问题,大约有300多个任务。刚开始使用的线程池为newCachedThreadPool,并设置最大阻塞时长60s。运行时发现,每次都要等到阻塞时间到达后被强制停止,才会执行之后的程序。后来改为newFixedThreadPool(10)来执行,就运行正常了。感觉很奇怪。通过日志打印,看到当使用newCachedThreadPool线程池的时候,会大约创建300多个线程,随着任务的增加,线程数也会增加,导致系统资源被大量占用,有的线程被阻塞住了,到设置到的最大阻塞时长时,任务被强制停止。因此,使用newCachedThreadPool的时候要根据实际任务运行时间情况,因为它可以创建最多Integer.MAX_VALUE个线程,反而会占用系统资源,降低运行效率。这就是为什么官方文档中会说:newCachedThreadPool会大幅度提高 大量短暂异步任务的性能了





  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当需要执行许多短时间的任务时,可以使用 `ExecutorService` 的 `newCachedThreadPool()` 方法创建一个线程池对象。该线程池会根据需要自动创建新的线程,并在空闲一定时间后自动回收不再使用的线程,因此适用于任务数量不确定或任务执行时间短的情况。 下面是一个使用 `newCachedThreadPool()` 方法创建线程池对象的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class CachedThreadPoolExample { public static void main(String[] args) { // 创建一个可缓存的线程池对象 ExecutorService executor = Executors.newCachedThreadPool(); // 提交多个任务 for (int i = 0; i < 10; i++) { executor.execute(new Task(i)); } // 关闭线程池 executor.shutdown(); } static 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."); } } } ``` 在以上示例代码,我们首先使用 `Executors.newCachedThreadPool()` 方法创建了一个可缓存的线程池对象 `executor`,并提交了多个任务。每个任务都是一个简单的输出语句,用于模拟一个短时间的任务。 最后,我们调用了 `executor.shutdown()` 方法关闭线程池。需要注意的是,一旦调用了 `shutdown()` 方法,线程池将不再接受新的任务,但已经提交的任务将继续执行直到完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值