前言
java 创建线程方式一般都是通过线程池来创建,怎么使用线程池创建线程,使用
Executors
???,带着疑问往下看
常见线程池
//根据实际需要自动扩充
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//固定线程
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(6);
//单个线程
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
以上是常用的线程池,但是实际开发不推荐使用,建议使用原生线程池。为什么禁止使用Exectors创建线程,很多人听过这句话,但不知道为啥?分析一下
Executors.newCachedThreadPool()
创建线程代码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
对于
Executors.newCachedThreadPool()
没有设置线程数,遇强则强,不给值默认最大线程数Integer.MAX_VALUE
最大 21亿个线程,对于高并发是致命的
Executors.newFixedThreadPool(n)、Executors.newSingleThreadExecutor()
线程创建代码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
看完代码至此,你也会发现问题所在
虽然newSingleThreadExecutor/newSingleThreadExecutor
有线程数限制,但是他们都用的LinkedBlockingQueue
队列长度是Integer.MAX_VALUE
,阻塞21亿个线程,对于高并发场景简直是灾难
禁止使用Executors 线程池创建线程
禁止使用Executors 线程池创建线程
禁止使用Executors 线程池创建线程
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;
}
- corePoolSize: 核心线程数,具体设置根据实际设置密集、IO密集
- 高并发、任务执行时间短的业务,属于CPU密集型,设置和CPU核数一致,减少上下文切换
- 并发不高、任务执行时间长
- 如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务,
- 假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和CPU密集型一样,线程池中的线程数设置得少一些,减少线程上下文的切换
- 并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步。业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
- maximumPoolSize:最大线程数
- long keepAliveTime:存活时间
- TimeUnit unit:单位
- BlockingQueue workQueue:阻塞队列
- ThreadFactory threadFactory:线程工厂
- RejectedExecutionHandler: 拒绝策略
拒绝策略
前提线程达到最大线程数,队列也满了
- AbortPolicy分析
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
AbortPolicy默认策略,直接抛出异常,任务丢失
- 测试AbortPolicy
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
4,
60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 0; i < 7; i++) {
final int num = i;
threadPoolExecutor.execute(()->{
System.out.println("线程-"+Thread.currentThread().getName()+" 任务:"+(num+1));
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
最大线程数4,队列是2,最大处理6个线程请求,多出部分抛弃
- CallerRunsPolicy 分析
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
多出的任务,不会丢弃,由调用者去执行
- 测试CallerRunsPolicy
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1,
2,
60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
try {
for (int i = 0; i < 7; i++) {
final int num = i;
threadPoolExecutor.execute(() -> {
System.out.println("线程-" + Thread.currentThread().getName() + " 任务:" + (num + 1));
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
最大处理线程数4个,剩余任务return 到线程调用者main 去执行
- DiscardOldestPolicy分析
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
丢弃比较老的任务,加入新的任务
- 测试DiscardOldestPolicy
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1,
2,
60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try {
for (int i = 0; i < 7; i++) {
final int num = i;
threadPoolExecutor.execute(() -> {
System.out.println("线程-" + Thread.currentThread().getName() + " 任务:" + (num + 1));
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
喜新厌旧,抛弃未执行旧任务,加入新的
- DiscardPolicy分析
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
什么都不干,不处理,不抛异常
- 测试DiscardPolicy
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1,
2,
60,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardPolicy()
);
try {
for (int i = 0; i < 7; i++) {
final int num = i;
threadPoolExecutor.execute(() -> {
System.out.println("线程-" + Thread.currentThread().getName() + " 任务:" + (num + 1));
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPoolExecutor.shutdown();
}
}
DiscardPolicy 拒绝策略很佛系,任务丢了,啥也不干
总结
java 线程池总结起来 7大参数,4种策略。默认是AbortPolicy。
- AbortPolicy:丢任务,抛异常
- CallerRunsPolicy:不丢任务,多余任务调用者去执行
- DiscardOldestPolicy: (喜新厌旧),丢弃长时间未执行任务,新任务腾地,不抛异常
- DiscardPolicy:(佛系),丢任务,不抛异常