线程池的优点
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁带来的消耗。
- 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行。
- 提高线程的可管理性:使用线程池可以统一进行线程分配、调度和监控。
一、线程池的实现原理
1. 线程池的处理流程
是否满等价于是否都在执行任务
2.ThreadPoolExecutor执行execute()方法的流程
二、线程池的使用
1. 线程池的创建
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
2. 向线程池提交任务
方法一:execute,用于不需要返回值的任务,无法判断任务是否成功
方法二:submit,用于提交需要返回值的任务
3. 关闭线程池
方法一:shutdown
方法二:shutdownNow
4. 合理配置线程池
- 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
- 任务的优先级:高、中和低。
- 任务的执行时间:长、中和短。
- 任务的依赖性:是否依赖其他系统资源,如数据库连接。
性质不同的任务可以用不同规模的线程池分开处理,CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太
大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。
三、Executor框架
1. ThreadPoolExecutor详解
- public static ExecutorService newCachedThreadPool创建无大小限制的线程池,适用于任务多,执行时间短或者服务器负载比较小
public class TestCachedThread {
public static void main(String[] args) {
final AtomicInteger atomicInteger = new AtomicInteger(0);
//缓存线程
ExecutorService e = Executors.newCachedThreadPool();
for (int i = 0; i < 10_100; i++) {
e.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "="+atomicInteger.getAndAdd(1));
}
});
}
e.shutdown();
}
}
- public static ExecutorService newFixedThreadPool(int nThreads)适用于创建固定的线程池,用于负载比较高的时候
public class TestFixThread {
public static void main(String[] args) {
final AtomicInteger atomicInteger = new AtomicInteger(0);
//固定容量的线程池
ExecutorService e = Executors.newFixedThreadPool(5);
for (int i = 0; i < 100; i++) {
e.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "="+atomicInteger.getAndAdd(1));
}
});
}
e.shutdown();
}
}
- public static ExecutorService newSingleThreadExecutor适用于单线程,保证顺序执行任务,使用CPU的一个核
public class TestSingleThread {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
final AtomicInteger atomicInteger = new AtomicInteger(1);
for (int i = 0; i < 10; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "=" + atomicInteger.getAndAdd(1));
}
});
}
executorService.shutdown();
}
}
- 定时调度ScheduledThreadPoolExecutor
为了实现周期性的执行任务,对ThreadPoolExecutor做了如下的修改。
1.使用DelayQueue作为任务队列。
2.获取任务的方式不同
3.执行周期任务后,增加了额外的处理
public class TestScheduled {
public static void main(String[] args) {
ScheduledExecutorService e=Executors.newScheduledThreadPool(5, new ThreadFactory() {
private final AtomicInteger atomicInteger=new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
thread.setName("调度任务"+atomicInteger.getAndAdd(1));
return thread;
}
});
e.schedule(new Runnable() {
@Override
public void run() {
System.out.println("this1-"+Thread.currentThread().getName());
}
},1,TimeUnit.SECONDS);
e.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("this2-"+LocalDateTime.now().toString()+" "+Thread.currentThread().getName());
}
},1,2,TimeUnit.SECONDS);
e.shutdown();
}
}