通过Runnable接口来创建线程时,实现多个run任何时可以共享一个线程是如何实现的?比如说线程池

来源:3-7 本章习题

慕村0357006

2019-11-07

写回答

1回答

悟空

2019-11-07

原因:线程重用的核心是,线程池对Thread做了包装,不重复调用hread.start(),而是自己有一个Runnable.run(),run方法里面循环在跑,跑的过程中不断检查我们是否有新加入的子Runnable对象,有新的Runnable进来的话就调一下我们的run(),其实就一个大run()把其它小run()#1,run()#2,...给串联起来了。同一个Thread可以执行不同的Runnable,主要原因是线程池把线程和Runnable通过BlockingQueue给解耦了,线程可以从BlockingQueue中不断获取新的任务。

详细分析展开:

在ThreadPoolExecutor的execute方法,作用是添加将要执行的任务:

   

public void execute(Runnable command) {

   if (command == null)

       throw new NullPointerException();

   int c = ctl.get();

   if (workerCountOf(c) < corePoolSize) {

       if (addWorker(command, true))

           return;

       c = ctl.get();

   }

   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);

   }

   else if (!addWorker(command, false))

       reject(command);

}

   


分析:可以看出:ThreadPoolExecutor.execute()的功能就是:
1、将任务添加至阻塞队列workQueue,也就是workQueue.offer(command)
2、根据core和maxPool,选择是否创建Worker,也就是addWorker()

可以看出,线程复用的实现在Worker中,这里的Worker实现了Runnable接口,在重写的run方法里面执行了runWorker():

   

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);

    }

}

   

简化后的runWorker方法:

   

runWorker(Worker w) {

    Runnable task = w.firstTask;

    w.firstTask = null;

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

        try {

            task.run();

        } finally {

            task = null;

        }

    }

}

   

可以看出,这个方法的逻辑:

1、通过getTask()方法,获取待执行的任务。
2、通过task.run();执行具体的任务。
3、有while循环,会不停地执行。

在这里,我们找到了最终的实现,getTask方法从workQueue中取出了新任务,实现了复用,至此分析完毕。

注:getTask不仅可以取出任务,还可以控制线程数量,思路主要有两点:

1,如果当前活动线程数大于最大线程数或者等待超时,则进行Worker数目减少,也就是大于核心线程的就这样被销毁掉了。

2,如果当前活动线程数小于等于核心线程数,同样也是去缓存队列中取任务,但当缓存队列中没任务了,就会进入阻塞状态,直到能取出任务为止,因此这个线程是处于阻塞状态的,并不会因为缓存队列中没有任务了而被销毁。这样就保证了线程池有N个线程是活的,可以随时处理任务,从而达到重复利用的目的。


0
1
慕村0357006
好的,谢谢老师!
2019-11-07
共1条回复

线程八大核心+Java并发原理及企业级并发解决方案

完整的并发知识网络+丰富的工作内容分享+50余道并发高频面试题

2512 学习 · 939 问题

查看课程