一、什么是线程池?
线程池是一种线程的集合,线程池中包含了多个线程,这些线程可以轮流执行池中的任务,从而实现线程的复用,提高系统的效率,避免资源的浪费。线程池具有以下特点:
1、线程可以轮流执行,避免创建或销毁线程的开销
2、线程数量可以控制,防止系统因为线程过多而崩溃
3、线程池中的线程可以自动重用,不需要每次都重新创建线程
4、线程池可以根据不同的任务进行优先级排序,保证高优先级任务先执行
二、创建线程池的步骤
创建线程池的步骤如下:
1、创建一个线程池对象,设置线程池的属性,如核心线程数量、最大线程数量、线程空闲时间、等待队列长度、拒绝策略等等
2、创建一个等待队列,用于存放等待执行的任务
3、创建一个线程池管理器,用于管理线程池的状态,比如线程的创建、销毁、执行任务等
4、向线程池中添加任务,线程池会自动分配线程去执行任务
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MyThreadPool { public static void main(String[] args) { //创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(5); //添加任务到线程池 for(int i=0;i<10;i++){ final int task = i; executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"执行任务:"+task); } }); } //关闭线程池 executorService.shutdown(); } }
三、常见的线程池类型
Java中常见的线程池类型有如下几种:
1、FixedThreadPool:固定数量线程池,线程数量一旦设定就不会发生变化。
2、CachedThreadPool:缓存线程池,线程数量可以根据任务的多少自动调整。
3、SingleThreadExecutor:单线程线程池,只有一个线程在执行任务,适合于重要的任务需要顺序执行的场景。
4、ScheduledThreadPool:定时任务线程池,适用于需要按照一定的周期执行任务的场景。
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class MyScheduledThreadPool { public static void main(String[] args) { //创建定时任务线程池 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3); //延迟1秒后执行任务 scheduledExecutorService.schedule(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"执行任务"); } },1, TimeUnit.SECONDS); //每隔2秒执行一次任务 scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"执行任务"); } }, 0, 2, TimeUnit.SECONDS); //关闭线程池 scheduledExecutorService.shutdown(); } }
四、线程池的拒绝策略
当线程池中的线程已经全部被占用,等待队列已经满了,此时如果还有新的任务进来,线程池就需要执行拒绝策略。Java中提供了四种线程池的拒绝策略。
1、AbortPolicy:拒绝新任务并抛出异常
2、CallerRunsPolicy:将新任务提交给调用线程来执行
3、DiscardOldestPolicy:将等待队列中最早的任务丢弃,将新任务添加到等待队列尾部
4、DiscardPolicy:直接丢弃新任务
import java.util.concurrent.*; public class MyRejectedThreadPool { static class MyTask implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"执行任务"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { //创建线程池,设置拒绝策略为DiscardPolicy ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy()); //添加任务到线程池 for(int i=0;i<5;i++){ executorService.execute(new MyTask()); } //关闭线程池 executorService.shutdown(); } }
五、线程池的最佳实践
1、尽量设置合适的线程数量
线程数量过多会导致系统开销过大,过少会导致系统无法充分利用资源。
2、合理的等待队列长度
等待队列长度过长会导致系统处理速度变慢,过短会导致任务不能得到处理。
3、使用合适的拒绝策略
不同的应用场景需要使用不同的拒绝策略。
4、合理的任务划分与设计
任务应该尽可能合理的拆分,并根据优先级和执行时间进行排序。
六、总结
线程池是一种用于管理线程的机制,具有复用线程、控制线程数量、自动重用、以及优先级等特点,可以提高系统效率,避免资源浪费。在使用线程池时,要注意线程池的属性设置、拒绝策略的选择、以及任务的合理划分与设计,从而达到最佳的效果。