线程池的使用注意事项

一、Java 中的 Executors 类定义的快捷工具方法

Executors.newFixedThreadPool();
Executors.newCachedThreadPool();
Executors.newWorkStealingPool();
Executors.newScheduledThreadPool();
Executors.newSingleThreadExecutor();
Executors.newSingleThreadScheduledExecutor();

《阿里巴巴 Java 开发手册》中提到禁止使用这些方法。可以使用:

import jodd.util.concurrent.ThreadFactoryBuilder;

ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS,
       	new ArrayBlockingQueue<>(100),
        new ThreadFactoryBuilder().setNameFormat("demo-threadpool-%d").get(),
        new ThreadPoolExecutor.AbortPolicy());

executor.shutdown();

参数介绍:
new ThreadPoolExecutor(核心线程数, 最大线程数, 线程数超时时间, 空闲线程保留时间单位, 缓冲队列, 线程工厂-提供创建线程的功能, 异常处理策略);
默认情况下核心线程空闲是不会回收的,如果设置可以回收:
executor.allowCoreThreadTimeOut(true);

二、两种快捷工具的OOM 分析

Executors.newFixedThreadPool();

public static ExecutorService newFixedThreadPool(int var0) {
   return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}
public static ExecutorService newFixedThreadPool(int var0, ThreadFactory var1) {
   return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), var1);
}
public LinkedBlockingQueue() {
	this(2147483647);
}

-核心线程数 = 最大线程数,
-没有线程空闲超时时间
-LinkedBlockingQueue 相当于无界队列

如果任务较多并且执行较慢的话,队列可能会快速积压,撑爆内存导致 OOM!!!
Executors.newCachedThreadPool();

public static ExecutorService newCachedThreadPool() {
   return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}

public static ExecutorService newCachedThreadPool(ThreadFactory var0) {
   return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue(), var0);
}
public SynchronousQueue() {
   this(false);
}

-没有核心线程
-最大线程数是Integer.MAX_VALUE,相当于无上限
-SynchronousQueue 是一个没有存储空间的阻塞队列

如果任务的执行时间比较长,线程是需要分配一定的内存空间作为线程栈的,比如 1MB,因此无限制创建线程必然会导致 OOM!!!

核心线程是否已满 —> 任务队列是否已满 —> 最大线程是否已满 —> 任务队列是否已满 —> 执行策略

三、常用的workQueue类型:

1. SynchornousQueue()
public SynchronousQueue() {
	this(false);
}

接收到任务时直接交给线程处理,使用这个队列一般maxnumPoolSize 都会指定为Integer.MAX_VALUE,核心线程数corePoolSize一般为0
2. LinkedBlockingQueue()
public LinkedBlockingQueue() {
   this(2147483647);
}

当任务数大于核心线程数时,任务会被添加到队列中,该队列可以认为是无界的,所以会导致设置的最大线程数失效。
3. ArrayBlockingQueue()
public ArrayBlockingQueue(int var1) {
    this(var1, false);
}

public ArrayBlockingQueue(int var1, boolean var2) {
    this.itrs = null;
    if (var1 <= 0) {
        throw new IllegalArgumentException();
    } else {
    	//设置数组的大小
        this.items = new Object[var1];
        this.lock = new ReentrantLock(var2);
        this.notEmpty = this.lock.newCondition();
        this.notFull = this.lock.newCondition();
    }
}

可以限定队列的长度
4. DelayQueue
public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E> {
    private final transient ReentrantLock lock = new ReentrantLock();
    private final PriorityQueue<E> q = new PriorityQueue();
    private Thread leader = null;
    private final Condition available;

    public DelayQueue() {
        this.available = this.lock.newCondition();
    }
	//队列内元素必须实现 Delayed 接口
    public DelayQueue(Collection<? extends E> var1) {
        this.available = this.lock.newCondition();
        this.addAll(var1);
    }
	.......
}

队列内元素必须实现 Delayed 接口,这就意味着你传进去的任务必须先实现 Delayed 接口。
这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务。

四、拒绝策略:

1. AbortPolicy() > 默认策略

new ThreadPoolExecutor.AbortPolicy();
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() {
    }
	//抛出RejectedExecutionException 异常
    public void rejectedExecution(Runnable var1, ThreadPoolExecutor var2) {
        throw new RejectedExecutionException("Task " + var1.toString() + " rejected from " + var2.toString());
    }
}

该策略直接抛出RejectedExecutionException异常,该任务也不再执行。
2. CallerRunsPolicy()

new ThreadPoolExecutor.CallerRunsPolicy();
public static class CallerRunsPolicy implements RejectedExecutionHandler {
	public CallerRunsPolicy() {
	}
	
	public void rejectedExecution(Runnable var1, ThreadPoolExecutor var2) {
	    if (!var2.isShutdown()) {
	        var1.run();
	    }
	
	}
}

任务被拒绝之后会调用当前线程池所在的线程去执行被拒绝的任务。
这个策略的缺点就是可能会阻塞主线程。
3. DiscardPolicy()

new ThreadPoolExecutor.DiscardPolicy();
public static class DiscardPolicy implements RejectedExecutionHandler {
	public DiscardPolicy() {
	}
	
	public void rejectedExecution(Runnable var1, ThreadPoolExecutor var2) {
	}
}

被线程池拒绝的任务直接抛弃,不会抛异常也不会执行。
4. DiscardOldestPolicy()

new ThreadPoolExecutor.DiscardOldestPolicy();
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
	public DiscardOldestPolicy() {
	}
	
	public void rejectedExecution(Runnable var1, ThreadPoolExecutor var2) {
	    if (!var2.isShutdown()) {
	        var2.getQueue().poll();
	        var2.execute(var1);
	    }
	}
}

rejectedExecution先从任务队列总弹出最先加入的任务,空出一个位置,
然后再次执行execute方法把任务加入队列。

5. 自定义MyRejectedExecutionHandler
static class MyRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        new Thread(r,"新线程"+new Random().nextInt(10)).start();
    }
}

部分内容参考 原文链接.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值