多线程线程池

说到多线程,概念性东西就不一一赘述了,首先回顾下线程的创建。

Java线程创建的四种方式

1.继承Thread类,重写run方法

static class ThreadDemo extends Thread{
@Override
public void run() {
//super.run();
//业务代码......
}
}

public static void main(String[] args) {
ThreadDemo thread = new ThreadDemo();
thread.setDaemon(true);
thread.setName("thread_demo");
thread.start();
}

2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target

static class RunnableDemo implements Runnable{
@Override
public void run() {
//业务代码......
}
}

public static void main(String[] args) {
Thread thread = new Thread(new RunnableDemo());
thread.start();
}

3.通过Callable和FutureTask创建线程

public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableDemo callable = new CallableDemo();
FutureTask<Object> futureTask = new FutureTask<>(callable);
new Thread(futureTask)..start();
Object o = futureTask.get();
}

static class CallableDemo implements Callable<Object>{
@Override
public Object call() {
//业务代码......
return null;
}
}

可以看出Callable与Runable的区别在于Callable带有返回值且可以检测线程是否完成

4.通过线程池创建线程

static class ThreadDemo extends Thread{
@Override
public void run() {
//super.run();
//业务代码......
}
}

static class RunnableDemo implements Runnable{
@Override
public void run() {
//业务代码......
}
}

static class CallableDemo implements Callable<Object>{
@Override
public Object call() {
//业务代码......
return null;
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(5);

executorService.execute(new ThreadDemo());

executorService.execute(new RunnableDemo());

FutureTask<Object> futureTask = new FutureTask<>(new CallableDemo());
Future<?> submit = executorService.submit(futureTask);
submit.get();
}

说到线程池,Executor提供了四种线程池

1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

但安装编码规约插件的同学会发现用Executor创建线程池会爆红提示,当然也给出了解释:

image.png

找到源码点进去一探究竟

image.png

newFixedThreadPool除了设置了核心线程数和最大线程数,其他用的都是默认值。

那来了解下ThreadPoolExecutor的核心参数

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,

RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
  • corePoolSize:核心线程数
    核心线程会一直存活,及时没有任务需要执行
    当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
    设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

  • queueCapacity:任务队列容量(阻塞队列)
    当核心线程数达到最大时,新任务会放在队列中排队等待执行

  • maxPoolSize:最大线程数
    当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
    当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

  • keepAliveTime:线程空闲时间
    当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
    如果allowCoreThreadTimeout=true,则会直到线程数量=0

  • allowCoreThreadTimeout:允许核心线程超时

  • rejectedExecutionHandler:任务拒绝处理器
    当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
    当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
    实现RejectedExecutionHandler接口,可自定义处理器

参数设置不当是会出现oom的哦,所以要注意核心参数的默认值

corePoolSize=1
queueCapacity=Integer.MAX_VALUE
maxPoolSize=Integer.MAX_VALUE
keepAliveTime=60s
allowCoreThreadTimeout=false
rejectedExecutionHandler=AbortPolicy()

参数设置了,在饱和的情况下ThreadPoolExecutor的处理顺序是什么样子的呢?

  • 当线程数小于核心线程数时,创建线程。
  • 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  • 当线程数大于等于核心线程数,且任务队列已满
  • 若线程数小于最大线程数,创建线程
  • 若线程数等于最大线程数,抛出异常,拒绝任务

最后分享个自己在项目中常用的线程池创建工具类

@Slf4j
public class LocalThreadPool {

public final static String poolName = "thread_pool";

private volatile static LocalThreadPool singletonPool;

private ThreadPoolExecutor executor;

private ThreadPoolExecutor callable;

public static LocalThreadPool getInstance(){
if(singletonPool == null){
synchronized (LocalThreadPool.class){
if(singletonPool == null){
singletonPool = new LocalThreadPool();
}
}
}
return singletonPool;
}

private LocalThreadPool(){

//runnable
final AtomicInteger runnableId = new AtomicInteger(0);

ThreadFactory runableFactory = new ThreadFactory() {

@Override
public Thread newThread(Runnable r) {

Thread thread = new Thread(r,"thread_pool_executor_"+runnableId);

thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("{}:{}",t.getName(),e);
}
});
return thread;
}
};

//callable
final AtomicInteger callableId = new AtomicInteger(0);

ThreadFactory callableFactory = new ThreadFactory() {

@Override
public Thread newThread(Runnable r) {

Thread thread = new Thread(r,"thread_pool_callable"+callableId);

thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {

@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("{}:{}",t.getName(),e);
}
});
return thread;
}
};

executor = new ThreadPoolExecutor(10,20,60,TimeUnit.SECONDS,

new LinkedBlockingQueue<>(20),runableFactory,new RejectedExecutionHandler(){

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if(!executor.isShutdown()){
r.run();
log.info("caller run runnable");
}
}
});
callable = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(20),
callableFactory, new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if(!executor.isShutdown()){
r.run();
log.info("caller run callable");
}
}
});
}
public void execute(Runnable r){
executor.execute(r);
}
public <T> Future<T> submit(Callable<T> c){
return callable.submit(c);
}
}

用起来非常之方便

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

推荐阅读更多精彩内容