juc - 线程池之七大参数介绍和正确的构造线程池

线程池之七大参数

我们知道,线程池的核心类是 ThreadPoolExecutor,构造一个ThreadPoolExecutor需要7个参数!


image.png
int corePoolSize

线程池中的常驻核心线程数

int maximumPoolSize

线程池中能够容纳同时执行的最大线程数,此值必须大于等于1

long keepAliveTime

多余的空闲线程的存活时间。当前池中的线程数量超过corePoolSize时,多余线程会被销毁到只剩下corePoolSize个线程为至

TimeUnit unit

keepAliveTime的单位

BlockingQueue<Runnable> workQueue

任务队列,被提交但未被执行的任务。由阻塞队列实现,关于阻塞队列我这篇文章有讲解 https://www.jianshu.com/p/862c93ab3203

ThreadFactory threadFactory

表示生产线程池中工作线程的工厂。用于创建线程,一般默认即可

RejectedExecutionHandler handler

拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程数(maximumPoolSize)时如何来拒绝请求执行的runnable饿策略。
我的这篇文章有详细的讲解拒绝策略
https://www.jianshu.com/p/bf6fabb7b459

实际上我们应该如何使用线程池?

不允许直接使用jdk自带的三种线程池

Executors.newCachedThreadPool();
Executors.newFixedThreadPool(5);
Executors.newSingleThreadExecutor();

工作中请使用自定义的写法:直接使用ThreadPoolExecutor类带上7个参数构造线程池!因为可能导致oom异常,所谓oom异常就是 java.lang.OutOfMemoryError:Java heap space

image.png

实际上请使用ThreadPoolExecutor类带上7个参数来构造线程池
  • 注意指定队列的长度,我这里是new LinkedBlockingQueue<>(3)。如果不指定3,则队列长度为Integer.MAX_VALUE,可能因为任务的积累导致oom。

    image.png

  • maximumPoolSize在cpu密级型项目中请配置成 cup核心数+1==>Runtime.getRuntime().availableProcessors()+1
    maximumPoolSize在io密级型项目中请配置成 cpu核心数/阻塞系数

参数调整试验

使用如下代码来构造线程池。2个核心线程,5个最大数量,3个等待队列席位。

        ExecutorService executorService =
                new ThreadPoolExecutor(
                        2,
                        5,
                        2, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());
实验一,任务数量为6
import java.util.concurrent.*;

public class Test {

    public static void main(String[] args) {
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        try {
            for (int i = 1; i <= 6; i++) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + " 办理业务");
                    }
                });
            }
        } finally {
            executorService.shutdown();
        }

    }
}

这里有6个任务。所以线程池的核心线程、等待队列都被用完。还剩下1个任务(6-2-3=1),需要创建线程来执行。所以执行结果中打印了pool-1-thread-3,该线程即是线程池临时创建出来的线程!


image.png
实验二,任务数量为9
package com.springboot.study.demo1;
import java.util.concurrent.*;
class Test1 {

    public static void main(String[] args) {
        //2个核心线程 等待数量席位3个  最大线程数5个,那么最大容纳任务是8个
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
        try {
            //提交9次任务,超出的1个任务会被应用拒绝策略
            for (int i = 1; i <= 9; i++) {
                executorService.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName() + " 办理业务");
                    }
                });
            }
        } finally {
            executorService.shutdown();
        }
    }
}

执行结果:抱错。此时任务数量9超过线程池可接受的最大任务数8(maximumPoolSize 3 +workQueue的容量5)

Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task Test$1@42110406 rejected from java.util.concurrent.ThreadPoolExecutor@531d72ca[Running, pool size = 5, active threads = 5, queued tasks = 3, completed tasks = 0]

创建自定义线程工厂为线程池中线程命名

创建线程池时使用自定义的线程工厂,这样可以为该线程池内的线程改名

package io.renren;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class UserThreadFactory implements ThreadFactory {
    private final String namePrefix;
    private final AtomicInteger nextId = new AtomicInteger(1);
    // 定义线程组名称,在 jstack 问题排查时,非常有帮助
     public UserThreadFactory(String whatFeaturOfGroup) {
        namePrefix = "来自 UserThreadFactory 线程工厂的 " + whatFeaturOfGroup + "-线程-";
    }
    @Override
    public Thread newThread(Runnable task) {
        String name = namePrefix + nextId.getAndIncrement();
        Thread thread = new Thread(null, task, name, 0);
        System.out.println(thread.getName());
        return thread;
    }
}

class TestFa{

    public static void main(String[] args) {

        UserThreadFactory userThreadFactory = new UserThreadFactory(" 用户 ");

        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(
                        2,
                        5,
                        2, TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        userThreadFactory ,
                        new ThreadPoolExecutor.AbortPolicy());

        for (int i = 0; i <8 ; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        threadPoolExecutor.shutdown();


    }

}
ThreadFactory 匿名
        ThreadFactory threadFactory = new ThreadFactory() {
            AtomicInteger nextId = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable task) {
                Thread thread = new Thread(null, task, "测试redis分布式锁线程" + nextId.getAndIncrement(), 0);
                return thread;
            }
        };

        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(2,
                        5,
                        3,
                        TimeUnit.MINUTES,
                        new ArrayBlockingQueue<>(5),
                        threadFactory,
                        new ThreadPoolExecutor.AbortPolicy()
                );

        for (int i = 0; i <10 ; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {

                    System.out.println(Thread.currentThread().getName());

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

推荐阅读更多精彩内容