zoukankan      html  css  js  c++  java
  • java使用默认线程池踩过的坑(二)

    云智慧(北京)科技有限公司 陈鑫

    是的。一个线程不可以启动两次。那么它是怎么推断的呢?
    public synchronized void start() {
    /**
    * A zero status valuecorresponds to state “NEW”. 0相应的是state NEW
    */
    if (threadStatus!= 0) //假设不是NEW state,就直接抛出异常!
    throw newIllegalThreadStateException();
    group.add(this);
    boolean started = false;
    try {
    start0(); // 启动线程的native方法
    started = true;
    } finally {
    try {
    if (!started) {
    group.threadStartFailed(this);
    }
    } catch(Throwable ignore) {
    }
    }
    }
    恩,仅仅有是NEW状态才可以调用native方法启动一个线程。好吧。到这里了。就普及也自补一下jvm里的线程状态:
    全部的线程状态::
    l NEW —— 还没有启动过
    l RUNNABLE —— 正在jvm上执行着
    l BLOCKED —— 正在等待锁/信号量被释放
    l WAITING —— 等待其它某个线程的某个特定动作
    l TIMED_WAITING —— A thread that iswaiting for another thread to perform an action for up to a specified waitingtime is in this state.
    l TERMINATED —— 退出,停止
    线程在某个时间点上仅仅可能存在一种状态。这些状态是jvm里的,并不反映操作系统线程的状态。查一下Thread的API,没有对其状态进行改动的API。那么这条路是不通的吗?
    细致考虑一下……
    假设把任务做成Runnable实现类,然后在把这个实现类丢进线程池调度器之前,利用此Runnable构造一个Thread,是不是这个Thread对象就行控制这个runnable对象。进而控制在线程池中执行着的task了呢?非也!让我们看看Thread和ThreadPoolExecutor对Runnable的处理吧。
    Thread
    /* What will berun. */
    private Runnabletarget;
    结合上面的start()方法,非常easy猜出,start0()会把target弄成一个线程来进行执行。


    ThreadPoolExecutor
    public void execute(Runnable command){
    if (command== null)
    thrownew NullPointerException();
    int c =ctl.get();
    if(workerCountOf(c) < corePoolSize) {
    if (addWorker(command, true))
    return;
    c =ctl.get();
    }
    if(isRunning(c) && workQueue.offer(command)) {
    intrecheck = ctl.get();
    if (!isRunning(recheck) && remove(command))
    reject(command);
    else if(workerCountOf(recheck) == 0)
    addWorker(null, false);
    }
    else if (!addWorker(command, false))
    reject(command);
    }

    private boolean addWorker(RunnablefirstTask, boolean core) {

    booleanworkerStarted = false;
    booleanworkerAdded = false;
    Worker w =null;
    try {
    finalReentrantLock mainLock = this.mainLock;
    w = newWorker(firstTask);
    finalThread t = w.thread;
    if (t!= null) {
    mainLock.lock();
    try{
    int c = ctl.get();
    int rs = runStateOf(c);

                   if (rs < SHUTDOWN ||
                       (rs == SHUTDOWN && firstTask == null)) {
                       if (t.isAlive()) // precheck that t is startable
                            throw newIllegalThreadStateException();
    

    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;
    }
    那么Worker又是如何的呢?
    Worker
    private final class Worker
    extendsAbstractQueuedSynchronizer
    implementsRunnable
    {
    finalThread thread;
    RunnablefirstTask;
    volatilelong completedTasks;
    Worker(Runnable firstTask) {
    setState(-1); //调用runWorker之前不可以interrupt
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
    }
    public voidrun() {
    runWorker(this);
    }
    ……
    …….
    voidinterruptIfStarted() {
    Threadt;
    if(getState() >= 0 && (t = thread) != null &&!t.isInterrupted()) {
    try{
    t.interrupt();
    }catch (SecurityException ignore) {
    }
    }
    }
    }
    可见worker里既包装了Runnable对象——task,又包装了一个Thread对象——以自己作为初始化參数。由于worker也是Runnable对象。

    然后对外提供了执行与停止接口,run()和interruptIfStarted()。回想上面使用Thread的样例不禁有了新的领悟,我们把一个Thread对象交给ThreadPoolExecutor执行后。实际的调用是对Thread(FileTask())对象,我们临时称之为workerWrapper。

    那么我们在池外进行FileTask.interrupt()操作影响的是FileTask对象。而不是workerWrapper。所以可能上面对于start()方法二次调用不是特别适当。更恰当的应该是在fileTask.interrupt()的时候就跑出异常,由于从来没有对fileTask对象执行过start()方法。这时候去interrupt就会出现错误。详细例如以下图:
    无
    分析到此,我们已经明白除了调用ThreadPoolExecutor了的interruptWorkers()方法别无其它途径操作这些worker了。
    private void interruptWorkers() {
    finalReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
    for(Worker w : workers)
    w.interruptIfStarted();
    } finally {
    mainLock.unlock();
    }
    }

  • 相关阅读:
    隔行扫描 和 逐行扫描
    CSS3--关于z-index不生效问题
    vue与其他框架对比
    跨域(转)
    vue 事件修饰符(阻止默认行为和事件冒泡)
    vue 3.0新特性
    bash leetcode
    数据库
    css排版
    盒模型
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/7026859.html
Copyright © 2011-2022 走看看