JUC基础之线程池

池化技术

程序运行的本质:占用系统资源
所以我们要优化资源的使用,就要用到池化技术(线程池,连接池,内存池,对象池…..)
事先准备好一些资源,有人呢要用 ,就来拿 ,用完还回来
好处:降低资源消耗,提高响应的速度,方便管理,线程复用,控制对大并发数,管理线程

Executors创建线程池的三个方法

//  Executors 工具类 3大方法
public class Demo
{
    public static void main(String[] args)
    {
       // ExecutorService executorService = Executors.newSingleThreadExecutor();//单个
       // ExecutorService executorService = Executors.newFixedThreadPool(5);//固定的 线程 池
        ExecutorService executorService = Executors.newCachedThreadPool();//可以伸缩的,遇强则强,遇弱则弱


        try
        {
            for (int i = 0; i < 10; i++)
            {
                //使用线程池创建线程
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        }
        finally
        {
            //线程池必须要关闭
            executorService.shutdown();
        }
    }
}

线程池的七个参数

看一下 newSingleThreadExecutor() 的源码

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

看一下 newFixedThreadPool(int nThreads) 的源码

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

看一下 newCachedThreadPool() 的源码

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

可以看到底层都是在 new 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;
}

看到了七个参数:

int corePoolSize: 核心线程池大小
int maximumPoolSize: 最大核心线程池大小
Long keepAliveTime: 超时了没有人调用就会释放
TimeUnit unit: 超时单位
BlockingQueue<Runnable> workQueue: 阻塞队列
ThreadFactory threadFactory: 线程工厂,创建线程,一般不用修改
RejectedExecutionHandler handler 拒绝策略

四种拒绝策略:

四种拒绝策略

Executors创建线程池拒绝策略默认是 AbortPolicy

AbortPolicy :超出了最大承载数量(队列满了)抛出异常

RejectedExecutionException :被拒绝执行异常

CallerRunsPolicy :超出了最大承载数量(队列满了)不抛出异常 哪来的回哪哪里去 也就是说线程池处理不了,返回给main执行

DiscardOldestPolicy : 超出了最大承载数量(队列满了)不抛出异常,尝试和最早的线程竞争,竞争失败了就放弃

DiscardPolicy : 超出了最大承载数量(队列满了)不抛出异常,放弃任务

再来看看三个方法的里面的int corePoolSize 和int maximumPoolSize 参数

newSingleThreadExecutor()
核心线程数:1
最大线程数:1

newFixedThreadPool(int nThreads)
核心线程数:nThreads
最大线程数:nThreads

newCachedThreadPool()
核心线程数:0
最大线程数:Integer.MAX_VALUE 也就是2147483647

[强制]线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:

  1. FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer .MAX_ _VALUE,可能会堆积大量的请求,从而导致00M。
  2. CachedThreadPool和ScheduledThreadPool: 允许的创建线程数量为Integer.MAX__VALUE,可能会创建大量的线程,从而导致00M。
    ———阿里巴巴开发手册

我们要用 ThreadPoolExecutor 来创建线程池

看看这个图会更好理解线程池

银行办理业务类比线程池

用代码实现一下

public class Bank
{
    public static void main(String[] args)
    {
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                2,   //默认两个柜台
                5, //最多五个柜台
                3, //柜台等待 3秒
                TimeUnit.SECONDS,  //等待单位
                new LinkedBlockingQueue<>(5), // 侯客区位置5
                Executors.defaultThreadFactory(),  //线程工厂
                new ThreadPoolExecutor.AbortPolicy()  //银行满了,还有人来,不予理睬,抛出异常
        );


        try
        {
            for (int i = 0; i <11 ; i++)
            {
              poolExecutor.execute(()->{
                  System.out.println(Thread.currentThread().getName()+"办理业务");
              });
            }
        }
        finally
        {
            poolExecutor.shutdown();
        }


    }
}


运行结果
pool-1-thread-1办理业务
pool-1-thread-4办理业务
pool-1-thread-3办理业务
pool-1-thread-2办理业务
pool-1-thread-4办理业务
pool-1-thread-3办理业务
pool-1-thread-5办理业务
pool-1-thread-1办理业务
pool-1-thread-4办理业务
pool-1-thread-2办理业务
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.y1.pool.Bank$$Lambda$1/363771819@10f87f48 rejected from java.util.concurrent.ThreadPoolExecutor@b4c966a[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 10]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
    at com.y1.pool.Bank.main(Bank.java:30)

可以看出来了11个“客户”,1 2 3 4 5 个柜台全部开始运行,但是超出了最大承载数量 (阻塞队列+最大核心线程池),因为拒绝策略设置了AbortPolicy,抛出错误RejectedExecutionException :被拒绝执行异常

最大线程该数设定的两种方式:

CPU密集型:几核CPU就可以设置线程数为几,效率最高,但是呢你不写死对吧…
所以呢要获取一下你的CPU核数:

Runtime.getRuntime().availableProcessors(); //获取CPU核数

IO密集型:判断程序中有多少个特别消耗IO的线程,设置最大线程大于这个数量

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。