好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java线程池复用线程的秘密你知道吗

前言

我们都知道 线程池 可以帮我们管理线程,重复利用线程执行不同的任务。正常情况下,我们创建的线程执行完任务后就会自行销毁,那么线程池是如何做到复用线程的呢?

源码探究

我们从线程池ThreadPoolExecutor源码入手,一探究竟。为了突出重点,以下的方法源码过滤了部分无关代码,以求逻辑清晰。

execute方法

那就从线程池执行的execute方法入手吧!来看一下方法的源码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public void execute(Runnable command) {

         if (command == null )

             throw new NullPointerException();       

         int c = ctl.get();

         //1.小于核心线程数时,创建线程

         if (workerCountOf(c) < corePoolSize) {

             if (addWorker(command, true ))

                 return ;

             c = ctl.get();

         }

         //2.达到核心线程数,不超过队列界限时,添加到队列

         if (isRunning(c) && workQueue.offer(command)) {

             int recheck = ctl.get();

             if (! isRunning(recheck) && remove(command))

                 reject(command);

             else if (workerCountOf(recheck) == 0 )

                 addWorker( null , false );

         }

         //3.队列已满,不超过最大线程数时,创建线程

         else if (!addWorker(command, false ))

         //4.达到最大线程数时,执行拒绝策略

             reject(command);

     }

线程池执行的4个步骤相信大家已经有所了解,这里我们只看添加线程的方法addWorker()

addWorker方法

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

private boolean addWorker(Runnable firstTask, boolean core) {

         boolean workerStarted = false ;

         boolean workerAdded = false ;

         Worker w = null ;

         try {

             //1.创建Worker,传入任务

             w = new Worker(firstTask);

             //2.取出执行任务的线程

             final Thread t = w.thread;

             if (t != null ) {

                 final ReentrantLock mainLock = this .mainLock;

                 mainLock.lock();

                 try {                   

                     int c = ctl.get();

                     if (isRunning(c) ||

                         (runStateLessThan(c, STOP) && firstTask == null )) {

                         if (t.getState() != Thread.State.NEW)

                             throw new IllegalThreadStateException();

                         workers.add(w);

                         workerAdded = true ;

                         int s = workers.size();

                         if (s > largestPoolSize)

                             largestPoolSize = s;

                     }

                 } finally {

                     mainLock.unlock();

                 }

                 if (workerAdded) {

                     //3.执行线程

                     t.start();

                     workerStarted = true ;

                 }

             }

         } finally {

             if (! workerStarted)

                 addWorkerFailed(w);

         }

         return workerStarted;

     }

参数解释:

core:true表示添加的是核心线程,false表示添加的非核心线程

这里大家只需要关心这3行加注释的代码就可以了

就是Worker管理了创建的线程和这个线程执行的第一个任务,并且在addWorker方法中调用线程的start方法,开启线程执行了任务。下面我们看看Worker这个类

Worker类

实现了Runnable接口

Worker 是线程池ThreadPoolExecutor的内部类,实现了Runnable接口

?

1

private final class Worker    extends AbstractQueuedSynchronizer    implements Runnable

重要属性

他有两个核心关键的属性,即封装了线程池的线程和要执行的任务,达到了线程和任务解耦的目的。

?

1

2

final Thread thread;

Runnable firstTask;

构造方法

addWorker方法会执行创建一个worker

?

1

w = new Worker(firstTask);

看一下Worker的构造方法

?

1

2

3

4

Worker(Runnable firstTask) {

     this .firstTask = firstTask;        

     this .thread = getThreadFactory().newThread( this );

}

可以看到 新创建的Worker本身也是一个Runnable,他的thread传的runnable任务就是worker本身

在addWorker方法,最终会取到worker的thread属性,然后启动这个thread

?

1

2

3

4

w = new Worker(firstTask);

final Thread t = w.thread;

... ...

t.start();

run方法

刚才介绍过,worker的thread的runnable参数传的就是worker本身,就是调的worker的run方法,现在我们来看最核心的worker的run方法

?

1

2

3

public void run() {

     runWorker( this );

}

调的是ThreadPoolExecutor的runWorker方法

?

1

2

3

4

5

6

7

8

9

final void runWorker(Worker w) {

     Runnable task = w.firstTask;

     w.firstTask = null ;

     try {

         while (task != null || (task = getTask()) != null )

         {          

             ...          

             task.run();

             ...

可以看到runWorker核心是一个while循环,执行了第一个task之后,就不停的从队列中取任务,直到没有任务了才会执行完,销毁线程

执行流程

execute方法调用addWorker方法,并且执行worker.thread.start()开启线程

​ ——》worker.thread 执行worker本身的run方法(worker实现了Runnable接口)

​ ——》执行ThreadPoolExecutor的runWorker方法,是个while循环,执行完worker本身的第一个任务之后,就不停从队列取任务,直到没有任务,执行完,退出循环,销毁

总结

线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。

在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去执行一个[循环任务],在这个[循环任务]中不停的检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的 run 方法,将 run 方法当成一个普通的方法执行,通过这种方式将只使用固定的线程就将所有任务的 run 方法串联起来。

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!             

原文链接:https://blog.csdn.net/Maybe_9527/article/details/123311127

查看更多关于Java线程池复用线程的秘密你知道吗的详细内容...

  阅读:13次