一、Executors创建线程
1、实例
使用线程池创建线程:
public class ExecutorTest {
public static void main(String[] args) {
ExecutorService thread = Executors.newSingleThreadExecutor();//创建单一线程
//ExecutorService thread = Executors.newFixedThreadPool(15);//创建多个线程
//ExecutorService thread = Executors.newCachedThreadPool();//根据情况创建线程
try {
for (int i = 0; i < 10; i++) {
thread.execute(()->{
System.out.println(Thread.currentThread().getName() + "执行");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
thread.shutdown();
}
}
}
2、ThreadPoolExecutor的七大参数
先了解一下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;
}
这个构造方法里有7大参数:
• corePoolSize 线程池的核心线程数
• maximumPoolSize 能容纳的最大线程数
• keepAliveTime 空闲线程存活时间
• unit 存活的时间单位
• workQueue 存放提交但未执行任务的队列
• threadFactory 创建线程的工厂类
• handler 等待队列满后的拒绝策略
四种拒绝策略:
3、三种方式的源码
看看其中源码:
newSingleThreadExecutor()的:这里设置核心线程数和能容纳的最大线程数为1
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
newFixedThreadPool()的:这里设置核心线程数和能容纳的最大线程数为自己设置的值
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
newCachedThreadPool()的:这里没有核心线程数,而且能容纳的线程数最大约为21亿,太大了可能会报OOM
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
所以不太推荐直接使用Executors创建线程,最好还是自己使用ThreadPoolExecutor构造器创建。
类比银行办理业务:corePoolSize就是正常工作的窗口,其他是忙的时候加的窗口,等待执行的任务队列类比一排排的等候区座椅。
4、创建线程和拒绝策略
public class ExecutorTest {
public static void main(String[] args) {
/**
* 核心线程数2
* 最大线程数5
* 空闲线程等待多久关闭5s
* 时间单位seconds
* 队列
* 默认创建线程,不用修改
* 拒绝策略:
* AbortPolicy:如果队列满了会抛异常,一般超过maxPoolSize + queue就超出了
* CallerRunsPolicy:如果队列满了会让你从哪里来去哪里
* DiscardPolicy:队列满了不会抛出异常,直接丢掉任务
* DiscardOldestPolicy:尝试和最早的竞争,也不会抛出异常,没争过就丢掉
*/
ThreadPoolExecutor thread = new ThreadPoolExecutor(
2,
5,
5,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy());
try {
for (int i = 0; i < 9; i++) {
thread.execute(()->{
System.out.println( Thread.currentThread().getName() + "执行");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
thread.shutdown();
}
}
}
如何设置最大线程数:
- cpu密集型:电脑有几核,就设置成多少
- io密集型:判断有多少个比较耗费资源的io,比这个数稍微大一点即可
CPU密集型: 这种任务一般不占用大量IO,所以后台服务器可以快速处理,压力落在CPU上。
I/O密集型:常有大数据量的查询和批量插入操作,此时的压力主要在I/O上。
- 与CPU密集型的关系:一般情况下,CPU核心数 == 最大同时执行线程数。在这种情况下(设CPU核心数为n),大量客户端会发送请求到服务器,但是服务器最多只能同时执行n个线程。所以这种情况下,无需设置过大的线程池工作队列,(工作队列长度 = CPU核心数 || CPU核心数+1)即可。
- 与I/O密集型的关系:由于长时间的I/O操作,导致线程一直处于工作队列,但它又不占用CPU,则此时有1个CPU是处于空闲状态的。所以,这种情况下,应该加大线程池工作队列的长度,尽量不让CPU空闲下来,提高CPU利用率。
一般说来,线程池的大小应该怎么设置(线程池初始的默认核心线程数大小?)(其中 N为CPU的个数 )。
- 如果是CPU密集型应用,则线程池大小设置为
N+1
, - 如果是IO密集型应用,则线程池大小设置为
2N+1
。
//查看电脑有多少核
System.out.println(Runtime.getRuntime().availableProcessors());