详解java线程池

前言

java 创建线程方式一般都是通过线程池来创建,怎么使用线程池创建线程,使用Executors???,带着疑问往下看

常见线程池

//根据实际需要自动扩充
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//固定线程
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(6);
//单个线程
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();

以上是常用的线程池,但是实际开发不推荐使用,建议使用原生线程池。为什么禁止使用Exectors创建线程,很多人听过这句话,但不知道为啥?分析一下

  • Executors.newCachedThreadPool() 创建线程代码
public static ExecutorService newCachedThreadPool() {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

对于 Executors.newCachedThreadPool() 没有设置线程数,遇强则强,不给值默认最大线程数 Integer.MAX_VALUE 最大 21亿个线程,对于高并发是致命的

  • Executors.newFixedThreadPool(n)、Executors.newSingleThreadExecutor() 线程创建代码
public static ExecutorService newSingleThreadExecutor() {
     return new FinalizableDelegatedExecutorService
         (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>()));
}

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

public LinkedBlockingQueue() {
    this(Integer.MAX_VALUE);
}

看完代码至此,你也会发现问题所在
虽然newSingleThreadExecutor/newSingleThreadExecutor 有线程数限制,但是他们都用的LinkedBlockingQueue 队列长度是Integer.MAX_VALUE,阻塞21亿个线程,对于高并发场景简直是灾难

禁止使用Executors 线程池创建线程
禁止使用Executors 线程池创建线程
禁止使用Executors 线程池创建线程

ThreadPoolExecutor

 public ThreadPoolExecutor(int corePoolSize, 
                              int maximumPoolSize, 
                              long keepAliveTime, 
                              TimeUnit unit, 
                              BlockingQueue<Runnable> workQueue, 
                              ThreadFactory threadFactory, 
                              RejectedExecutionHandler handler) {  
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • corePoolSize: 核心线程数,具体设置根据实际设置密集、IO密集
  • 高并发、任务执行时间短的业务,属于CPU密集型,设置和CPU核数一致,减少上下文切换
  • 并发不高、任务执行时间长
  1. 如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务,
  2. 假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和CPU密集型一样,线程池中的线程数设置得少一些,减少线程上下文的切换
  3. 并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步。业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
  • maximumPoolSize:最大线程数
  • long keepAliveTime:存活时间
  • TimeUnit unit:单位
  • BlockingQueue workQueue:阻塞队列
  • ThreadFactory threadFactory:线程工厂
  • RejectedExecutionHandler: 拒绝策略

拒绝策略

前提线程达到最大线程数,队列也满了

  • AbortPolicy分析
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
   throw new RejectedExecutionException("Task " + r.toString() +
                                         " rejected from " +
                                         e.toString());
}

AbortPolicy默认策略,直接抛出异常,任务丢失

  • 测试AbortPolicy
public static void main(String[] args) {

 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
           2, 
           4, 
           60, 
           TimeUnit.SECONDS,
           new LinkedBlockingDeque<>(2),
           Executors.defaultThreadFactory(), 
           new ThreadPoolExecutor.AbortPolicy());

   try {
       for (int i = 0; i < 7; i++) {
           final int num = i;
           threadPoolExecutor.execute(()->{
               System.out.println("线程-"+Thread.currentThread().getName()+" 任务:"+(num+1));
           });
       }
   } catch (Exception e) {
       e.printStackTrace();
   } finally {
       threadPoolExecutor.shutdown();
   }

}

在这里插入图片描述最大线程数4,队列是2,最大处理6个线程请求,多出部分抛弃

  • CallerRunsPolicy 分析
 /**
* Executes task r in the caller's thread, unless the executor
 * has been shut down, in which case the task is discarded.
 *
 * @param r the runnable task requested to be executed
 * @param e the executor attempting to execute this task
 */
 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    if (!e.isShutdown()) {
        r.run();
    }
}

多出的任务,不会丢弃,由调用者去执行

  • 测试CallerRunsPolicy
public static void main(String[] args) {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            1,
            2,
            60,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(2),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());

    try {
        for (int i = 0; i < 7; i++) {
            final int num = i;
            threadPoolExecutor.execute(() -> {
                System.out.println("线程-" + Thread.currentThread().getName() + " 任务:" + (num + 1));
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPoolExecutor.shutdown();
    }
}

在这里插入图片描述
最大处理线程数4个,剩余任务return 到线程调用者main 去执行

  • DiscardOldestPolicy分析
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
   if (!e.isShutdown()) {
        e.getQueue().poll();
        e.execute(r);
    }
}

丢弃比较老的任务,加入新的任务

  • 测试DiscardOldestPolicy
public static void main(String[] args) {
   ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            1,
            2,
            60,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(2),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardOldestPolicy()
    );

    try {
        for (int i = 0; i < 7; i++) {
            final int num = i;
            threadPoolExecutor.execute(() -> {
                System.out.println("线程-" + Thread.currentThread().getName() + " 任务:" + (num + 1));
            });
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        threadPoolExecutor.shutdown();
    }
}

在这里插入图片描述
喜新厌旧,抛弃未执行旧任务,加入新的

  • DiscardPolicy分析
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}

什么都不干,不处理,不抛异常

  • 测试DiscardPolicy
public static void main(String[] args) {
     ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
              1,
              2,
              60,
              TimeUnit.SECONDS,
              new LinkedBlockingDeque<>(2),
              Executors.defaultThreadFactory(),
              new ThreadPoolExecutor.DiscardPolicy()
      );

      try {
          for (int i = 0; i < 7; i++) {
              final int num = i;
              threadPoolExecutor.execute(() -> {
                  System.out.println("线程-" + Thread.currentThread().getName() + " 任务:" + (num + 1));
              });
          }
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          threadPoolExecutor.shutdown();
      }
}

在这里插入图片描述
DiscardPolicy 拒绝策略很佛系,任务丢了,啥也不干

总结

java 线程池总结起来 7大参数,4种策略。默认是AbortPolicy。

  • AbortPolicy:丢任务,抛异常
  • CallerRunsPolicy:不丢任务,多余任务调用者去执行
  • DiscardOldestPolicy: (喜新厌旧),丢弃长时间未执行任务,新任务腾地,不抛异常
  • DiscardPolicy:(佛系),丢任务,不抛异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值