线程池
线程池的作用
一般情况下线程运行就死亡了,后面如果有新任务就必须创建新的线程,如果有大量的任务就需要创建大量线程,会降低服务器的性能,造成资源的浪费。
线程池的作用是:首先会在池中分配一定数量的线程,线程使用完后会回到池中,等待下一个任务,线程资源就得到回收利用,减少服务器资源的消耗,提高了性能。
线程池的API
- Executor接口
- ExecutorService接口
- AbstractExecutorService抽象类
- ThreadPoolExecutor线程池类
- Executors工具类
Executors工具类
主要方法:
- ExecutorService newCachedThreadPool()
返回线程数量无限的线程池 - ExecutorService newFixedThreadPool(int size)
返回固定长度的线程池,好处是可以控制并发量 - ExecutorService newSingleThreadExecutor()
返回单一线程的线程池 - ScheduledExecutorService newScheduledThreadPool(int size)
返回可以调度的线程池
/**
* 测试长度不限的线程池
*/
public static void testCachedThreadPool(){
//获得长度不限的线程池
ExecutorService pool = Executors.newCachedThreadPool();
//测试执行100个任务
for(int i = 0;i < 100;i++){
final int n = i;
//使用线程池启动线程
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了! i --> "+n);
});
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//关闭线程池
pool.shutdown();
}
/**
* 测试长度固定的线程池
*/
public static void testFixedThreadPool(){
//固定长度线程池
ExecutorService pool = Executors.newFixedThreadPool(20);
for(int i = 0;i < 100;i++){
final int n = i;
//使用线程池启动线程
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了! i --> "+n);
});
}
//关闭线程池
pool.shutdown();
}
/**
* 测试长度单一的线程池
*/
public static void testSingleThreadPool(){
//单一长度线程池
ExecutorService pool = Executors.newSingleThreadExecutor();
for(int i = 0;i < 100;i++){
final int n = i;
//使用线程池启动线程
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了! i --> "+n);
});
}
//关闭线程池
pool.shutdown();
}
/**
* 测试可以调度的线程池
*/
public static void testScheduledThreadPool(){
//可调度线程池
ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
//执行线程,1、线程任务 2、初始的延迟数 3、周期数 4、时间单位
pool.scheduleAtFixedRate(()->{
System.out.println(Thread.currentThread().getName()+"执行了,时间:"+new Date());
}, 2, 1, TimeUnit.SECONDS);
}
自定义线程池的配置
ThreadPoolExecutor的构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue)
参数说明:
- corePoolSize 核心线程数
- maximumPoolSize 最大线程数
- keepAliveTime 临时的线程能够存活的时间
- unit 时间单位
- workQueue 用于保存任务的阻塞队列
优化配置:
- 核心线程数大于或等于CPU内核的数量(如果是CPU密集型任务,就需要尽量压榨CPU,参考值可以设为 NCPU+1,如果是IO密集型任务,参考值可以设置为2*NCPU)获得CPU的内核数:Runtime.getRuntime().availableProcessors()
- 最大线程数和核心线程数配置一样,性能比较高,因为避免临时线程的创建和销毁
- 如果临时线程比较多,可以将存活时间配置稍微长点,减少临时线程的创建和销毁
4、阻塞队列LinkedBlockingQueue的性能比较高
//cpu内核数
public static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
/**
* 自定义配置线程池
*/
public static void testThreadPool(){
//配置线程池
ExecutorService pool = new ThreadPoolExecutor(CPU_NUM,CPU_NUM * 4 + 20,
0,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
for(int i = 0;i < 100;i++){
final int n = i;
//使用线程池启动线程
pool.execute(()->{
System.out.println(Thread.currentThread().getName()+"执行了! i --> "+n);
});
}
//关闭线程池
pool.shutdown();
}
线程池的原理
- 线程池中线程是用什么保存
HashSet<Worker>
-
线程池运行的过程
-
线程池中的线程执行完任务后如何不死亡?
因为线程会从阻塞队列中不断取任务执行,一旦队列为空,线程会进入阻塞状态,一直到队列中有新的任务为止,线程的run方法不会结束。
结束
大家如果需要学习其他Java知识点,戳这里 超详细的Java知识点汇总