线程池(Thread Pool):把一个或多个线程通过统一的方式进行调度和重复使用的技术,避免了因为线程过多而带来使用上的开销
优点:(面试题)
可重复使用已有线程,避免对象创建、消亡和过度切换的性能开销。
避免创建大量同类线程所导致的资源过度竞争和内存溢出的问题。
支持更多功能,比如延迟任务线程池(newScheduledThreadPool)和缓存线程池(newCachedThreadPool)等。
创建方式:
有两种:ThreadPoolExecutor 和 Executors
1、ThreadPoolExecutor 的使用
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue(100)); threadPoolExecutor.execute(new Runnable() { @Override public void run() { // 执行线程池 System.out.println("Hello, Java."); } });
ThreadPoolExecutor构造方法:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
构造参数说明
//① corePoolSize
//线程池中的核心线程数,默认情况下核心线程一直存活在线程池中,
// 如果将 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设为 true,
// 如果线程池一直闲置并超过了 keepAliveTime 所指定的时间,核心线程就会被终止。
//② maximumPoolSize
// 线程池中最大线程数,如果活动的线程达到这个数值以后,后续的新任务将会被阻塞(放入任务队列)。
//③ keepAliveTime
//线程池的闲置超时时间,默认情况下对非核心线程生效,如果闲置时间超过这个时间,非核心线程就会被回收。
// 如果 ThreadPoolExecutor 的 allowCoreThreadTimeOut 设为 true 的时候,核心线程如果超过闲置时长也会被回收。
//④ unit
//配合 keepAliveTime 使用,用来标识 keepAliveTime 的时间单位。
// ⑤ workQueue
// 线程池中的任务队列,使用 execute() 或 submit() 方法提交的任务都会存储在此队列中。
//⑥ threadFactory 为线程池提供创建新线程的线程工厂。
//⑦ rejectedExecutionHandler
//线程池任务队列超过最大值之后的拒绝策略,RejectedExecutionHandler 是一个接口,里面只有一个 rejectedExecution 方法,可在此方法内添加任务超出最大值的事件处理。ThreadPoolExecutor 也提供了 4 种默认的拒绝策略:
//new ThreadPoolExecutor.DiscardPolicy():丢弃掉该任务,不进行处理
//new ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列里最近的一个任务,并执行当前任务
//new ThreadPoolExecutor.AbortPolicy():直接抛出 RejectedExecutionException 异常
//new ThreadPoolExecutor.CallerRunsPolicy():既不抛弃任务也不抛出异常,直接使用主线程来执行此任务
代码演示:
(1)test1
public static void test1() throws ExecutionException, InterruptedException { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.SECONDS, new LinkedBlockingQueue(2)); threadPoolExecutor.allowCoreThreadTimeOut(true); //线程池执行方法 execute() threadPoolExecutor.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"-Hello java!"); } }); //线程池执行方法 submit() 可以接收线程池执行的返回值。 Future<Object> future = threadPoolExecutor.submit(new Callable<Object>() { @Override public String call() throws Exception { System.out.println(Thread.currentThread().getName()+"-Hello china!"); return "success"; } }); System.out.println(future.get()); }
(2)test2
public static void test2(){ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,10,10L,TimeUnit.SECONDS, new LinkedBlockingQueue(2),new MyThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy()); for(int i=0;i<10;i++){ threadPoolExecutor.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(5000); System.out.println(Thread.currentThread().getName()+" wake up!"); } catch (InterruptedException e) { e.printStackTrace(); } } }); } }
static class MyThreadFactory implements ThreadFactory{ private AtomicInteger count = new AtomicInteger(0); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setName("myThread"+count.addAndGet(1)); return t; } }
(3)test3
//shutdown & shutdownNow //shutdown():不会立即终止线程池,而是要等所有任务队列中的任务都执行完后才会终止。执行完 shutdown 方法之后,线程池就不会再接受新任务了。 //shutdownNow():执行该方法,线程池的状态立刻变成 STOP 状态,并试图停止所有正在执行的线程,不再处理还在池队列中等待的任务,执行此方法会返回未执行的任务。 public static void test3(){ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,10,10,TimeUnit.SECONDS,new LinkedBlockingDeque<>(10)); threadPoolExecutor.execute(()->{ for (int j=0; j<5 ;j++){ System.out.println("i m " + Thread.currentThread().getName()); try { Thread.sleep(2000); } catch (InterruptedException e) { System.out.println(e.getMessage()); } } }); // threadPoolExecutor.shutdown(); threadPoolExecutor.shutdownNow(); threadPoolExecutor.execute(()->{ System.out.println("i m stop!"); }); }
(4)test4
public static void test4(){ ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1,1,10,TimeUnit.SECONDS,new LinkedBlockingDeque<>(1),new ThreadPoolExecutor.DiscardPolicy()); threadPoolExecutor.allowCoreThreadTimeOut(true); for (int i=0;i<10;i++) { threadPoolExecutor.execute(() -> { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(2000); System.out.println(Thread.currentThread().getName() + "wake up!"); } catch (InterruptedException e) { e.printStackTrace(); } }); } }
//线程池第 1 次执行任务时,会新创建任务并执行;第 2 次执行任务时,因为没有空闲线程所以会把任务放入队列;
//第 3 次同样把任务放入队列,因为队列最多可以放两条数据,所以第 4 次之后的执行都会被舍弃(没有定义拒绝策略),于是就打印了 3 次线程名称。
总结:
线程池的工作原理:
当线程池中有任务需要执行时,线程池会判断如果线程数量没有超过核心数量就会新建线程池进行任务执行,
如果线程池中的线程数量已经超过核心线程数,这时候任务就会被放入任务队列中排队等待执行;
如果任务队列超过最大队列数,并且线程池没有达到最大线程数,就会新建线程来执行任务;
如果超过了最大线程数,就会执行拒绝执行策略。
线程池可通过 submit() 来调用执行,从而获得线程执行的结果,也可以通过 shutdown() 来终止线程池。