Java JUC并发之线程池详解(重点)

十一、线程池(重点)

线程池 : 三大方法、7大参数、4种拒绝策略

池化技术

程序运行 => 本质:占用系统的资源!

如何优化资源的使用? => 池化技术

常用的池:

  • 线程池
  • 连接池
  • 内存池
  • 对象池
  • ...

注意: 经常创建、销毁 会造成资源浪费

池化技术: 事先准备好一些资源,如果有人要用,就从这里拿,用完之后还回来

线程池的好处

  • 降低资源的消耗
  • 提高相应的速度
  • 方便管理,防止内存泄漏

即 : 线程复用、可以控制最大并发数、管理线程

线程池不允许使用executors去创建,而是通过ThreadPoolExecutor的方式

(让写的同学更加明确线程池的运行规则,规避资源耗尽的风险)详细说明见阿里巴巴Java开发手册

Executors 返回的线程池对象的弊端:

  • FixedThreadPool和SingleThreadPool:

    允许的请求队列长度为Integer.MAX_VALUE => 21亿左右,会堆积大量的请求,导致OOM 【OOM : OutOfMemory,内存溢出】

  • CachedThreadPool 和 ScheduledThreadPool :

    允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM.

    三大方法:

    • Executors.newSingleThreadExecutor() => 单一线程
    • Executors.newFixedThreadPool(5) => 固定大小的线程池
    • Executors.newCachedThreadPool() => 线程池大小由CPU处理能力决定 (弹性变化)
    • Executors.newScheduledThreadPool(10) => 创建定长的线程池,而且支持定时以及周期性的任务执行
    package com.liu.threadpool;
    
    import java.util.concurrent.Executor;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    // Executors 工具类 四大方法
    public class TestThreadPool {
    
        public static void main(String[] args) {
    
            //ExecutorService threadPool = Executors.newSingleThreadExecutor();  // 单个线程
            //ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建固定数目的线程
            //ExecutorService threadPool = Executors.newCachedThreadPool(); // 根据CPU的能力来创建线程 遇强则强
            ExecutorService threadPool = Executors.newScheduledThreadPool(10);// 创建定长的线程池,而且支持定时以及周期性的任务执行
    
    
            try {
                for (int i = 1; i <= 10000; i++) {
                    threadPool.execute(()->{
                        System.out.println(Thread.currentThread().getName() + " ok");
                });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                threadPool.shutdown();
            }
        }
    }
    

线程池面试题!

四种实现方式 七大参数 四大拒绝策略

源码分析:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 21亿
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

本质 : ThreadPoolExecutor() !!!

public ThreadPoolExecutor(int corePoolSize, 
                          int maximumPoolSize, 
                          long keepAliveTime, 
                          TimeUnit unit, 
                          BlockingQueue<Runnable> workQueue ) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), 
         defaultHandler);
}
                         
    public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
                              int maximumPoolSize, // 最大核心线程池大小
                              long keepAliveTime, // 空闲线程的存活时间
                              TimeUnit unit, // 超时等待的时间单位,时间到了会将线程释放,只留下核心线程
                              BlockingQueue<Runnable> workQueue, // 阻塞队列
                              ThreadFactory threadFactory, // 线程工厂
                              RejectedExecutionHandler handler // 拒绝策略) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }

理解线程池的原理: 以银行办理业务为例:

手动创建线程池

package com.liu.threadpool;

import java.util.concurrent.*;

/**
 * 四种拒绝策略
 * 1. new ThreadPoolExecutor.AbortPolicy()  拒绝策略 银行满了,还有客人进来,不处理该业务并抛出异常
 * 2. new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略 哪来的去哪里! 由main线程处理
 * 3. new ThreadPoolExecutor.DiscardPolicy() // 拒绝策略 队列满了 不会抛出异常! 直接丢掉任务
 * 4. new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略 队列满了 尝试和最早的(执行了最长时间的线程)竞争,最后失败了也不会抛出异常
 */

public class TestThreadPool02 {
    public static void main(String[] args) {

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3), // 阻塞队列
                Executors.defaultThreadFactory(), // 线程工厂
                new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略 队列满了 尝试和最早的(执行了最长时间的线程)竞争,最后失败了也不会抛出异常
                );


        try {
            // 最大承载 :Queue + Max
            for (int i = 1; i <= 9; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

四种拒绝策略

  • AbortPolicy() 银行满了,还有客人进来,不处理该业务并抛出异常
  • CallerRunsPolicy() 哪来的去哪里! 由main线程 或其他线程 处理
  • DiscardPolicy() 队列满了 不会抛出异常! 直接丢掉任务
  • DiscardOldestPolicy() 队列满了 尝试和当前最老的线程执行的任务竞争,最后失败了也不会抛出异常

小结和拓展

  • 最大线程到底该如何设置? 依据什么来定义?(调优!)

    • CPI 密集型 CPU为多少核,就将最大线程池大小定义为多少

      可以保持CPU的效率最高!

       // 获取CPU的核数
              // CPU密集型、IO密集型
             System.out.println(Runtime.getRuntime().availableProcessors());
      
    • IO 密集型 **判断程序中十分占用IO资源的的线程数 **一般设置为 2 倍

      程序=> 15个大型任务 IO 十分占用资源 => 设置为 30

package com.liu.threadpool;

import java.util.concurrent.*;
public class TestThreadPool02 {
    public static void main(String[] args) {

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(), // cpu核数 即最大线程池大小
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3), // 阻塞队列
                Executors.defaultThreadFactory(), // 线程工厂
                new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略 队列满了 尝试和最早的(执行了最长时间的线程)竞争,最后失败了也不会抛出异常
                );


        try {
            // 最大承载 :Queue + Max
            for (int i = 1; i <= 9; i++) {
                // 使用了线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}
posted @ 2021-07-15 17:25  夕立君  阅读(105)  评论(0编辑  收藏  举报