后来想了一想,觉得最重要的东西没有说,下面的内容我自己隔了一段时间甚至都有些没理解,主要现在想说的最重要的东西,就是线程的start和run,start是启动一个新线程,run是执行线程的方法,复用就是把多个不同的run放啊写到一个线程的run内,然后start这个线程,如果提前写入,其实也不算是复用,线程池实现的是一个动态的复用,就是在线程执行过程中,动态的从一个阻塞队列中拿出任务,添加到线程run方法内,从而实现复用.最重要的就是这个吧.
今天是我第三次看线程池的源码,才敢写下这一点点理解,虽然我的博客一般都是自己学习的笔记和心得,但是毕竟是在互联网上,有需求的同行看到了,还是不能害人.虽说是第三次看,但是对其中一些很细节的条件分支还不是很理解,只是从宏观上稍微理解了作者的一些思路.下面说一下:
在我的理解中,池化技术,无论是连接池还是线程池,核心就是复用二字.池化技术往简单了说,就是重复利用一些比较"昂贵"的资源.
Java内的线程池有一系列的实现继承关系,到最高层的实现类是ThreadPoolExecutor
类.这个线程池如何实现复用,是我最关心的,但看了之后发现,其实很简单.
这个类实现复用就是用一个内部类Worker
,首先,这个内部类实现了Runnable
,还继承了AbstractQueuedSynchronizer
类,对于后者,暂时只需要知道它是负责Worker内部独占锁机制的,我们注意的还是Runnable,我们都知道正常创建一个线程的几种方法,实现Runnable就是其中一种方式.
实现Runnable并不奇怪,但是实现的同时,Worker内部还有一个Thread类型的属性.这两件事单独看,都很正常,但放在一起,就让我很费解,然后看一下构造器,这里贴一下源码:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
参数很熟悉,和我们使用实现Runnable的类创建线程的道理一样,不管其他,核心是最后一句,
this.thread = getThreadFactory().newThread(this);
这句什么意思,getThreadFactory().newThread()
可以看做我们使用的new Thread()
,看参数是this,也就是用Worker创建一个线程,然后指向Woker内部的Thread.
到这里,我想说一下我的学习时的疑惑,我再想,为什么要这样写,没想通,我换了个方向,如果不这样写,我又没有别的方法实现复用,别的方法和源码方法对立,那就是把thread和runnable实现分开写,从线程池的角度来看,我需要创建一个类似Worker类但是没有实现Runnable接口,实现runnable实现作为任务存放在阻塞队列中,然后我的类Worker类构造器也类似,不过不在将this作为创建线程的参数,而是使用传入的runnable.如何启动呢?使用类worker.thread.start()吗?这只能执行传入的那个,就算我再指定我的类Worker内部的runnable任务,我该怎么知道上一个线程是否执行完,在外部写一个死循环,在循环内部不断执行当前任务,执行完毕后再从队列中获取等待的任务,再执行吗?又有了问题,这个循环写在哪里?是不是每一个类Worker都得一个循环?这个时候我突然意识到一件事,如果我这样写,每次的任务如何执行,还是thread.start()吗?根据我的理解,start()是执行一个线程,它和直接运行runnable实现内的run方法是不同的,start涉及到操作系统的系统调用,它会启动一个新线程,我这样写根本没有实现复用.这样,我才深深的理解了源代码写法的深意.
源代码中之所以如此写,是我暂时想到实现复用的唯一方法,将整个Worker作为一个任务提交到本对象中的Thread对象执行,这时我在Worker的run方法中执行多个任务的run方法,这才是复用.
胡扯了这么多,可能大家忽略了Worker实现runnable,那就必须重写run方法,看一下源代码:
public void run() { runWorker(this); }
就一行,runWorker(this),这样什么都看不出来,点进去;
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
其实这段代码不难,但是很多变量必须结合上下文,所以我就简单说一下这段代码干了什么,
首先,拿到Worker对象中的实例化时传入的firstTask,顾名思义,不解释了,然后进入一个循环,循环内部,就是执行当前task.run方法,也就是说真正的执行任务,需要注意的是,每次循环中,是被上了锁的,是独占的,执行完当前任务时,执行getTask(),从任务队列中拿到任务,再次执行,直到没有任务才关闭这个线程.
这篇博客暂时就到这里了,我没有对整个线程池进行源码解析,我也没那个能力,只是说说我自己对复用的理解,希望可以帮助到有次困惑的同行.