private static int balance = 0;
public static void addBalance(int amount,Lock lock){
System.out.println("currentThread is "+Thread.currentThread().getName()+" try to get lock");
lock.lock();
try {
System.out.println("currentThread is "+Thread.currentThread().getName()+" locked");
balance+=amount;
}finally {
lock.unlock();
System.out.println("currentThread is "+Thread.currentThread().getName()+" unlocked");
}
}
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(0,10,0, TimeUnit.NANOSECONDS,
new ArrayBlockingQueue<Runnable>(100));
Lock lock = new ReentrantLock();
for(int i=0;i<100;i++){
// CompletableFuture.runAsync(()->addBalance(1,lock));
executor.submit(()->addBalance(1,lock));
}
while(balance<100){
System.out.println("Time waiting");
Thread.sleep(1000);
}
System.out.println(balance);
}
之后main函数一直未退出
dump下来未发现死锁啥的
但是守护线程有9个
搜了一波,结果如下
- 线程池的创建的时候,第一次 submit 操作会创建 Worker 线程(负责去拿任务处理),该线程里写了一个死循环,所以这个 Worker 线程不会死
- Worker 线程在创建的时候,被设置成了 非守护线程 , thread.setDaemon(false)
- 早在 JDK1.5 的时候,就规定了当所有非守护线程退出时, JVM 才会退出, Main 方法主线程和 Worker 线程都是非守护线程,所以不会死。
为什么Worker线程不会死
梦开始的地方先从初始化开始
//该方法利用多台实例化了一个ThreadPoolExecutor线程池,该线程池继承了一个抽象类AbstractExecutorService
ExecutorService service = Executors.newFixedThreadPool(10);
//调用了ThreadPoolExecutor.submit方法也就是父类的AbstractExecutorService.submit,该方法内部会去调用execute()方法
service.submit(() -> System.out.println("Hello "));
于是我们定位到ThreadPoolExecutor类的execute方法,我截取了部分如下,注意代码中我打注释的地方
public void execute(Runnable command) {
...
//如果工作线程还没有超过核心线程数
if (workerCountOf(c) < corePoolSize) {
//去添加工作线程
if (addWorker(command, true))
return;
}
...
线程池把每一个运行任务的工作线程抽象成了Worker,我们定位到内部addWorker方法
...
//新建一个Worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//下面的操作是将线程添加到工作线程集合里
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//如果添加成功的话
if (workerAdded) {
//把工作线程跑起来
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
这时候一个工作线程也就跑起来了,可以去执行任务了,我们定位到ThreadPoolExecutor的内部类Worker的run方法里
//该类调用了runWorker方法
public void run() {
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,会看这个Worker有没有任务,如果没有就会去取,这里是一个死循环,然后我们定位到getTask()方法,看他是怎么取任务的
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
...
}
这里解释了,工作线程其实不会死(超时时间不在本期范围内),我们继续定位到内部的getTask()方法,看他是怎么取任务的
private Runnable getTask() {
...
//有没有设置核心线程超时时间(默认没有)当前工作的线程数大于了线程池的核心线城市
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
...
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//调用workQueue的Take方法,WorkQueue默认是一个BlockingQueue,所以调用take方法会导致当前工作线程阻塞掉,指到拿到
workQueue.take();
//如果拿到任务就返回
if (r != null)
return r;
timedOut = true;
...
小结:
这里想说的有两点:
工作线程不会死(不设置线程存活时间,默认情况下),会一直拿任务,所以工作线程会一直活着
工作线程拿任务的时候,默认情况下,因为用的是BlockingQueue的take()拿不到任务会阻塞
Worker线程如何被设置成非守护线程
首先我们来到ThreadPoolExecutor的构造方法里
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
构造器里传入了一个ThreadFactory也就是Executors.defaultThreadFactory(),用来产生工作线程,一步一步的点进去我们会定位到Executors内部类DefaultThreadFactory的newThread方法
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
//关键代码是这里,把线程设置成了非守护线程
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
然后我们看ThreadPoolExector方法去new Worker()的时候
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
//这里的ThreadPool,就是上面提到的那个生产非守护线程的线程工厂
this.thread = getThreadFactory().newThread(this);
}
看上面的注释下面的内容,为什么是非守护线程就真相大白了。
最后将最开始的代码中,线程池的核心线程数改成0,那么JVM就会自动退出了
网友评论