jdk源码解析--ThreadPoolExecutor类

Posted 我的IT技术路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk源码解析--ThreadPoolExecutor类相关的知识,希望对你有一定的参考价值。

java中,线程池无论是在面试中或者是在实际使用中,都是一个高频出现问题,我们需要很好的了解和掌握java线程池。在了解这个线程池之前,我们先看下java中定义的几个关于线程池的类和它们的关系:ExecutorExecutorsExecutorServiceAbstractExecutorServiceThreadPoolExecutor。先看下类的继承关系图:

 


上面的图还是比较明显的看出这个类的关系:

Executor:该接口定义了向线程池提交任务执行的方法

ExecutorService:该接口继承Executor,并且丰富了线程池的其他操作,比如线程池的关闭,线程池允许有返回值的提交等

AbstractExecutorService:该抽象类是ExecutorService的一个实现类,该类中定义了一些具体的方法,该类中的一个常用方法是将execute方法封装成submit返回一个future对象,换句话说该方法允许线程带返回值。

ThreadPoolExecutor:这个类是真正线程池的实现类,它实现了AbstractExecutorService这个抽象类

Executors:该类是线程池的辅助类,里面提供了很多静态方法可以直接用来创建线程池,在jdk中,很多带s后缀的一般都是一个辅助类,比如arrays(数组辅助类),collections(集合辅助类),这些类里面会提供一些便捷的静态方法帮助大家实现类似的功能。

看完上面的类关系图之后,我们需要说明一下java线程池的工作原理(线程池的经典原理图):


这是线程池的经典原理图:

1.  当提交任务到线程池执行的时候,会先到核心线程开始执行

2. 如果核心线程满了会到阻塞队列进行排队,当核心线程完成当前任务时,会尝试从阻塞队列中获取待执行的任务

3. 当阻塞队列满了之后,会扩充线程数到最大线程数,执行当前任务

4. 当最大线程数满了之后,会进入拒绝策略,jdk提供了几种常用的拒绝策略,常用的是拒绝,拒绝最老的,以提交当前任务的线程执行该任务。

 

当我们了解了线程池的工作原理之后,我们可以尝试通过构建一个线程池,并且执行相应的任务,下面通过一个demo来说明一下线程池的简单使用。

1. import java.util.Random;  

2. import java.util.concurrent.*;  

3. import java.util.concurrent.atomic.AtomicInteger;  

4. import java.util.concurrent.atomic.AtomicLong;  

5. import java.util.stream.IntStream;  

6.   

7. public class TheadPoolTest {  

8.     public static final Random r = new Random();  

9.     public static void main(String[] args) {  

10.         ThreadFactory threadFactory = new ThreadFactory() {  

11.             final AtomicLong aLong = new AtomicLong(0);  

12.             @Override  

13.             public Thread newThread(Runnable r) {  

14.                 Thread thread = new Thread(r);  

15.                 thread.setDaemon(true);//设置守护线程  

16.                 thread.setName("Test-Thread-"+aLong.incrementAndGet());//设置线程名  

17.                 return thread;  

18.             }  

19.         };  

20.         AtomicInteger data =new AtomicInteger(0);  

21.         ThreadPoolExecutor executor = new ThreadPoolExecutor(  

22.                 5,//核心线程数  

23.                 10,//最大线程数  

24.                 10, TimeUnit.SECONDS,//线程空闲的最大等待时间  

25.                 new ArrayBlockingQueue<>(100),//阻塞队列  

26.                 threadFactory,//线程工程,一般线程工厂用来设置特定的线程名,设置是否是守护线程  

27.                 new ThreadPoolExecutor.AbortPolicy()//拒绝策略  

28.         );  

29.         IntStream.range(0,10).forEach(i->{  

30.             Future<Boolean> future = executor.submit(new AddTask());  

31.             try {  

32.                 if (future.get()){  

33.                     data.incrementAndGet();  

34.                 }  

35.             } catch (InterruptedException e) {  

36.             } catch (ExecutionException e) {  

37.             }  

38.         });  

39.         System.out.println("大于5的次数是:"+data.get());  

40.     }  

41.   

42.     public static class AddTask implements Callable<Boolean>{  

43.   

44.         @Override  

45.         public Boolean call() throws Exception {  

46.             int result = r.nextInt(10);  

47.             System.out.println(Thread.currentThread().getName()  

48.                     +" get result is " +result);  

49.             return result>5;  

50.         }  

51.     }  

52. }  

上面使用代码比较简单,我们通过每个线程执行的返回值来判断每个线程的随机值是否大于5,最后统计出大于5的线程的个数。上面的例子我们使用了callable,而不是runnable,这两者的主要区别在于线程执行的返回值。我们看下最后的执行结果:

 


本节内容就先到这里,下节内容开始,我们会描述线程池的底层源码的实现。


以上是关于jdk源码解析--ThreadPoolExecutor类的主要内容,如果未能解决你的问题,请参考以下文章

HashMap putVal 源码解析-JDK1.8

JDK1.8源码解析-HashMap

JDK源码及其他框架源码解析随笔地址导航

JDK源码之HashMap源码解析

追踪解析 jdk Proxy 源码

jdk下httpserver源码解析