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的线程,设置最大线程大于这个数量

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355