JDK-Thread线程池

Thread线程池

简述

jdk的并发相关类,在java.util.concurrent中,主要包括:

  • 线程安全的集合,ConcurrentHashMap
  • 阻塞队列,如LinkedBlockingDeque
  • 线程池,ThreadPoolExcutor
  • AQS锁,如ReentrantLock
  • 线程安全的原子类,如AtomicInteger

线程池

image-20210208003342503

线程与任务

public static void main(String[] args) {
    new Thread(()->{
        System.out.println("hello world!");
    }).start();
}

如上代码,线程与任务绑定,并且线程只能使用一次,导致频繁创建和销毁和销毁线程,消耗不必要的系统资源

所有就有了线程池,将线程和任务解耦。线程池负责维护执行任务的线程。

核心接口:Executor

public interface Executor {
    //Executor的实现类负责执行任务,客户端不在直接与Thread交流
    void execute(Runnable command);
}
简单的复用线程实现,查考
public class SerialExecutor2 implements Executor {

    private final Worker worker;
    private final Thread thread;
    boolean started = false;

    public SerialExecutor2() {
        this.worker = new Worker();
        this.thread = new Thread(worker);
    }
    @Override
    public synchronized void execute(Runnable r) {
        if (started) {
            this.worker.addTask(r);
        } else {
            this.started = true;
            this.worker.addTask(r);
            this.thread.start();
        }

    }
}
public class Worker implements Runnable {
    final Queue<Runnable> tasks = new ArrayDeque<>();
    
    public void addTask(Runnable runnable) {
        tasks.add(runnable);
    }
    @Override
    public void run() {
        Runnable active;
        while (true){
            if((active = tasks.poll())!=null){
                active.run();
            }
        }
    }
}
//测试代码
public static void main(String[] args) {
    SerialExecutor2 serialExecutor = new SerialExecutor2();
    serialExecutor.execute(()->{
        System.out.println(1);
    });
    serialExecutor.execute(()->{
        System.out.println(2);
    });
    serialExecutor.execute(()->{
        System.out.println(3);
    });
}
  1. Worker实现Runnable接口,该接口维护一个Runnable队列,并且其run方法为死循环,不断的从队列中获取任务并执行
  2. Executor的实现类,负责创建一个线程,execute负责启动线程执行Worker,以及向Worker不断添加任务

这样就实现了复用线程

第二版,与上面类似,但更贴近ThreadPoolExecutor源码实现

public class SerialExecutor2 implements Executor {

    private final Worker worker;
    private final Thread thread;
    final Queue<Runnable> tasks = new ArrayDeque<>();
    boolean started = false;

    public SerialExecutor2() {
        this.worker = new Worker();
        this.thread = new Thread(worker);
    }
    @Override
    public synchronized void execute(Runnable r) {
        if (started) {
            this.worker.addTask(r);
        } else {
            this.started = true;
            this.worker.addTask(r);
            this.thread.start();
        }

    }
    class Worker implements Runnable {
        void addTask(Runnable runnable) {
            tasks.add(runnable);
        }
        @Override
        public void run() {
            Runnable active;
            while (true){
                if((active = tasks.poll())!=null){
                    active.run();
                }
            }
        }
    }
}

上面实现的缺点:

  1. 线程数量固定
  2. 没有办法回收线程
  3. 任务队列线程不安全
  4. 没有拒绝策略

其实上面的缺点也就是线程池必备的参数条件

ThreadPoolExecutor

接口:ExecutorService

public interface ExecutorService extends Executor {
	//######################关闭线程池#######################
    //执行已经接受的task,但不在接受新的task
    void shutdown();
    /**
     * 尝试停止正在执行的任务,挂起正在执行的任务,并返回。
     * 并不会等待正在执行的任务完成
     * 并不保证能正确的停止正在执行的任务,比如如果该方法的实现是调用Thread#interrupt,执行的Task并不一定会响应interrupt方法,因此也就不会终止
     */
    List<Runnable> shutdownNow();
    //在调用shutdown()后阻塞直到所有任务执行完成,或出现超时,超时的线程会被interrupted
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
    //###################线程池状态的判断#####################################
    //返回executor是否关闭状态
    boolean isShutdown();
    // 在调用shutdown()和shutdownNow()后所有的任务都执行完成,则isTerminated()为true
    boolean isTerminated();

    //#################提交Callable或Runnable接口############################
    // 提交Callable接口,通过Future.get()阻塞获取执行结果
    <T> Future<T> submit(Callable<T> task);
    //执行返回的结果类型
    <T> Future<T> submit(Runnable task, T result);
    //提交Callable接口实现,Future.get()返回null
    Future<?> submit(Runnable task);

    //######################提交Callable或Runnable集合#######################
    //提交Callable接口的集合,当所有任务complete返回Futures列表
    //complete可以是正常执行完,也可以是throwing an exception
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        throws InterruptedException;
    //执行给定的task集合,返回future列表
    //Future#isDone为true,任务complete或者timeout
    //超时时间是针对的所有tasks
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;
    //执行给定的 task 集合,返回执行成功的结果
    //只要其中一个task执行完了,就会立即返回,其他task就会退出.比如多线程查找某个存在的值
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;
    //执行给定的task集合,指定超时时间返回执行成功的结果
    //超时时间是针对的所有tasks
    <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
  • 关闭线程池
  • 提交Callable或Runnable接口
  • 提交Callable或Runnable集合
  • 线程池状态的判断

线程状态和线程数量

//ctl具备两个作用,
//workerCount:低29位作为工作线程的计数,
//runState:而高3位记录线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//29
private static final int COUNT_BITS = Integer.SIZE - 3;
//00011111 11111111 11111111 11111111
//低29位全部转换为1,用于异或运算
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

// runState 存储高3位中
//并且RUNNING小于0,即SHUTDOWN
//RUNNING:		11100000 00000000 00000000 00000000
//SHUTDOWN:		00000000 00000000 00000000 00000000
//STOP:			00100000 00000000 00000000 00000000
//TIDYING:		01000000 00000000 00000000 00000000
//TERMINATED:	01100000 00000000 00000000 00000000
/**
 *   RUNNING:  Accept new tasks and process queued tasks
 *   SHUTDOWN: Don't accept new tasks, but process queued tasks
 *   STOP:     Don't accept new tasks, don't process queued tasks,
 *             and interrupt in-progress tasks
 *   TIDYING:  All tasks have terminated, workerCount is zero,
 *             the thread transitioning to state TIDYING
 *             will run the terminated() hook method
 *   TERMINATED: terminated() has completed
*/
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

//取高3位的,实际该方法未使用过
private static int runStateOf(int c)     { return c & ~COUNT_MASK; }
//取低29位
private static int workerCountOf(int c)  { return c & COUNT_MASK; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

private static boolean runStateLessThan(int c, int s) {
    return c < s;
}

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

状态转换图

图片描述

构造方法关键参数

/**
 * 阻塞队列,保存等待被执行的task
 * workQueue.isEmpty()判断是否为空
 * workQueue.poll(long) 指定等待时间内获取task,超时返回null
 * workQueue.take()阻塞直到返回task
 */
private final BlockingQueue<Runnable> workQueue;

/**
 * 对HashSet<Worker> workers的操作都必须持有mainLock
 */
private final ReentrantLock mainLock = new ReentrantLock();

/**
 * Set containing all worker threads in pool. Accessed only when
 * holding mainLock.
 */
private final HashSet<Worker> workers = new HashSet<>();

/**
 * Wait condition to support awaitTermination.
 */
private final Condition termination = mainLock.newCondition();

/**
 * Tracks largest attained pool size. Accessed only under
 * mainLock.
 */
private int largestPoolSize;

/**
 * Counter for completed tasks. Updated only on termination of
 * worker threads. Accessed only under mainLock.
 */
private long completedTaskCount;

//所有用户控制的参数均应使用volatiles声明,因此正在进行的操作可以使用最新值而不需要lock锁

// 线程工厂,所有线程都是通过addWorker()方法调用ThreadFactory创建的
private volatile ThreadFactory threadFactory;

//拒绝策略
private volatile RejectedExecutionHandler handler;

//单位:nanoseconds,空闲线程存时间
//大于核心线程部分会使用该事件
//或者allowCoreThreadTimeOut设置为true时,也会使用该时间
private volatile long keepAliveTime;

// 核心线程数是否使用keepAliveTime作为空闲的存活时间
private volatile boolean allowCoreThreadTimeOut;

//核心线程数
private volatile int corePoolSize;

//最大线程数
private volatile int maximumPoolSize;

//默认拒绝策略
private static final RejectedExecutionHandler defaultHandler =
	new AbortPolicy();
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    //###省略参数校验###
    //核心线程数量
    this.corePoolSize = corePoolSize;
    //最大线程数量
    this.maximumPoolSize = maximumPoolSize;
    //阻塞队列,等待被执行的task
    this.workQueue = workQueue;
    //线程所允许的空闲时间
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    //线程工厂,默认Executors.defaultThreadFactory()
    this.threadFactory = threadFactory;
    //拒绝策略
    this.handler = handler;
    //AbortPolicy:直接抛出异常,这是默认策略;
    //CallerRunsPolicy:用调用者所在的线程来执行任务;
    //DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
    //DiscardPolicy:直接丢弃任务;
}

task添加与执行

execute
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
  
    // 前面说的那个表示 “线程池状态” 和 “线程数” 的整数
    int c = ctl.get();
  
    // 如果当前线程数少于核心线程数,那么直接添加一个 worker 来执行任务,
    // 创建一个新的线程,并把当前任务 command 作为这个线程的第一个任务(firstTask)
    if (workerCountOf(c) < corePoolSize) {
        // 添加任务成功,那么就结束了。提交任务嘛,线程池已经接受了这个任务,这个方法也就可以返回了
        // 至于执行的结果,到时候会包装到 FutureTask 中。
        // 返回 false 代表线程池不允许提交任务
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 到这里说明,要么当前线程数大于等于核心线程数,要么刚刚 addWorker 失败了
  
    // 如果线程池处于 RUNNING 状态,把这个任务添加到任务队列 workQueue 中
    if (isRunning(c) && workQueue.offer(command)) {
        /* 这里面说的是,如果任务进入了 workQueue,我们是否需要开启新的线程
         * 因为线程数在 [0, corePoolSize) 是无条件开启新的线程
         * 如果线程数已经大于等于 corePoolSize,那么将任务添加到队列中,然后进到这里
         */
        int recheck = ctl.get();
        // 如果线程池已不处于 RUNNING 状态,那么移除已经入队的这个任务,并且执行拒绝策略
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程池还是 RUNNING 的,并且线程数为 0,那么开启新的线程
        // 到这里,我们知道了,这块代码的真正意图是:担心任务提交到队列中了,但是线程都关闭了
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果 workQueue 队列满了,那么进入到这个分支
    // 以 maximumPoolSize 为界创建新的 worker,
    // 如果失败,说明当前线程数已经达到 maximumPoolSize,执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}
runWorker()
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    // new Worker()是state==-1,此处是调用Worker类的tryRelease()方法,将state置为0, 而interruptIfStarted()中只有state>=0才允许调用中断
    w.unlock(); // allow interrupts
    ////是否“突然完成”,如果是由于异常导致的进入finally,那么completedAbruptly==true就是突然完成的
    boolean completedAbruptly = true;
    try {
        //getTask()方法控制线程的存活时间
        while (task != null || (task = getTask()) != null) {
            //上锁,不是为了防止并发执行任务,为了在shutdown()时不终止正在运行的worker
            w.lock();
/**
* clearInterruptsForTaskRun操作
* 确保只有在线程stoping时,才会被设置中断标示,否则清除中断标示
* 1、如果线程池状态>=stop,且当前线程没有设置中断状态,wt.interrupt()
* 2、如果一开始判断线程池状态<stop,但Thread.interrupted()为true,即线程已经被中断,又清除了中断标示,再次判断线程池状态是否>=stop
*   是,再次设置中断标示,wt.interrupt()
*   否,不做操作,清除中断标示后进行后续步骤
*/
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                //进入这里说明线程池已经为stop状态了
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                try {
                    task.run();
                    afterExecute(task, null);
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}

这里很重要的一点是,task = getTask()不为空,说明已经将task从队列中取出来,那么即使线程为stop,或者线程为interrupt状态,该任务仍然被执行。考虑shutdownNow()会返回没有执行的task列表,这么做也很合理。但是在下一个循环getTask()就为空了。

getTask()
/**
 * Performs blocking or timed wait for a task, depending on
 * current configuration settings, or returns null if this worker
 * must exit because of any of:  以下情况会返回null
 * 1. There are more than maximumPoolSize workers (due to
 *    a call to setMaximumPoolSize).
 *    超过了maximumPoolSize设置的线程数量(因为调用了setMaximumPoolSize())
 * 2. The pool is stopped.
 *    线程池被stop
 * 3. The pool is shutdown and the queue is empty.
 *    线程池被shutdown,并且workQueue空了
 * 4. This worker timed out waiting for a task, and timed-out
 *    workers are subject to termination (that is,
 *    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
 *    both before and after the timed wait.
 *    线程等待任务超时
 *
 * @return task, or null if the worker must exit, in which case
 *         workerCount is decremented
 *         返回null表示这个worker要结束了,这种情况下workerCount-1
 */
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();

        // Check if queue empty only if necessary.
/**
* 对线程池状态的判断,两种情况会workerCount-1,并且返回null
* 线程池状态为shutdown,且workQueue为空(反映了shutdown状态的线程池还是要执行workQueue中剩余的任务的)
* 线程池状态为stop(shutdownNow()会导致变成STOP)(此时不用考虑workQueue的情况)
*/
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
            decrementWorkerCount();//循环的CAS减少worker数量,直到成功
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        //是否判断超时
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        //1.大于最大连接数且当前可用线程数>1,则返回,并减少worker数量
        //2.大于最大核心数且超时(在循环下一个周期,向阻塞队列取task时会等待一定时间),可用线程>1
        //3.大于最大连接数,且阻塞队列为空
        //4.大于核心数且超时,且阻塞队列为空
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            //这里满足上面的条件,将减少线程,比如超时
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        //1.阻塞从队列中获取task
        //2.或者阻塞一定时间获取task
        try {
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
            if (r != null)
                return r;
            timedOut = true;//取不到task会在下一个循环才退出
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

添加task流程图

图4 任务调度流程

线程中断

public class Thread{
    //中断线程,设置中断标记
    public void interrupt() {
        if (this != Thread.currentThread()) {
            checkAccess();

            // thread may be blocked in an I/O operation
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupt0();  // set interrupt status
                    b.interrupt(this);
                    return;
                }
            }
        }
        interrupt0();
    }
    //返回当前中断状态,并清除当前状态
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    //返回当前中断状态,不清除当前状态。如果当前中断状态为true,则再次调用也是true
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
    //ClearInterrupted表示是否清除原来的状态
    //如果为true,则下次调用返回为false,除非调用之前再次中断
    private native boolean isInterrupted(boolean ClearInterrupted);
}

线程中断,并不是真的停止/中断线程,只是设置中断标识,让线程自己在合适的时机中断自己。

阻塞方法区分为:

  • 不可中断的阻塞

    • java.io包中的同步Socket I/O
    • java.io包中的同步I/O
    • Selector的异步I/O
    • 获取某个锁
  • 可中断的阻塞,会返回InterruptedException

    • Thread.sleep()

    • Object.wait()

    • BlockingQueue.put()/take()

    • Thread.join()

    • 基于java.nio.channels.InterruptibleChannel接口的Io

      protected final void begin() {
          if (this.interruptor == null) {
              this.interruptor = new Interruptible() {
                  public void interrupt(Thread target) {
                      synchronized(AbstractInterruptibleChannel.this.closeLock) {
                          if (!AbstractInterruptibleChannel.this.closed) {
                              AbstractInterruptibleChannel.this.closed = true;
                              AbstractInterruptibleChannel.this.interrupted = target;
      
                              try {
                                                             AbstractInterruptibleChannel.this.implCloseChannel();
                              } catch (IOException var5) {
                              }
      
                          }
                      }
                  }
              };
          }
      
          blockedOn(this.interruptor);
          Thread me = Thread.currentThread();
          if (me.isInterrupted()) {
              this.interruptor.interrupt(me);
          }
      
      }
      
      

      会实现 sun.nio.ch.Interruptible接口,判断isInterrupted()中断状态,继而中断线程

    • 基于java.nio.channels.Selector接口的Io

      protected final void begin() {
          if (this.interruptor == null) {
              this.interruptor = new Interruptible() {
                  public void interrupt(Thread ignore) {
                      AbstractSelector.this.wakeup();
                  }
              };
          }
      
          AbstractInterruptibleChannel.blockedOn(this.interruptor);
          Thread me = Thread.currentThread();
          if (me.isInterrupted()) {
              this.interruptor.interrupt(me);
          }
      
      }
      

    同样实现了 sun.nio.ch.Interruptible接口,这里会直接调用wakeup(),直接唤醒selector

可中断阻塞即在方法实现中会判断线程中断状态,而不可中断阻塞则不会判断阻塞状态。

比如InputStream.read方法. 在检测到输入数据可用, 到达流末尾或者抛出异常前, 该方法一直阻塞. 而且阻塞的时候不会检查中断标记, 所以中断线程无法使read从阻塞状态返回. 但是关闭流可以使得read方法抛出异常, 从而从阻塞状态返回. Java Concurrency In Practice 7.1的归纳和总结

示例1
public static void main(String[] args) throws InterruptedException {
    Thread t1= new Thread(() -> {
        int count=0;
        while (true) {
            System.out.println(count);
        }
    });
    t1.start();
    Thread.sleep(1000);
    t1.interrupt();
    System.out.println("完成");
}

没有阻塞操作,也没有判断线程状态。此时线程会一直执行

示例2
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        int count = 0;
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println(count);
        }
    });
    t1.start();
    Thread.sleep(1000);
    t1.interrupt();
    System.out.println("完成");
}

没有阻塞操作,但此执行任务都判断线程是否中断。此时1秒后,线程中断后退出。

示例3
public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        int count = 0;
        while (!Thread.currentThread().isInterrupted()) {
            System.out.println(count);
            try {
                synchronized (ThreadMain.class) {
                    ThreadMain.class.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("被中断");
            }
        }
    });
    t1.start();
    Thread.sleep(1000);
    t1.interrupt();
    System.out.println("完成");
}
//特别注意这里打印了两个0,所以中断后状态isInterrupted()仍为false
0
完成
java.lang.InterruptedException
	at java.base/java.lang.Object.wait(Native Method)
	at java.base/java.lang.Object.wait(Object.java:328)
	at com.awesomeJdk.myNetty.exec.v3.ThreadMain.lambda$main$0(ThreadMain.java:17)
	at java.base/java.lang.Thread.run(Thread.java:834)
被中断
0
参考
public static void main(String[] args) throws InterruptedException {
    Runnable runnable=()-> {
        while(!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(1000); // 延迟1秒
                System.out.println(" 中断状态:"+Thread.currentThread().isInterrupted());
            } catch (InterruptedException e) {
                System.out.println(" 被中断:"+e.getMessage());
                System.out.println(" 异常后状态:"+Thread.currentThread().isInterrupted());
                //interrupt();
                //System.out.println(getName()+" 再次中断,状态:"+isInterrupted());
            }
        }
        System.out.println("任务退出!");
    };
    Thread thread = new Thread(runnable);
    thread.start();
    Thread.sleep(50);
    thread.interrupt();
    System.out.println("完成");
}
完成
 被中断:sleep interrupted
 异常后,中断状态:false
 中断状态:false
 中断状态:false

如果线程正在执行wait,sleep,join方法,你调用interrupt()方法,会抛出InterruptedException异常。而一个抛出了异常的线程的状态马上就会被置为非中断状态,如果catch语句没有处理异常,则下一次循环中isInterrupted()为false,线程会继续执行,可能你N次抛出异常,也无法让线程停止。

所以如上面代码注释,必须在catch中再次中断

示例4
//阻塞io不响应interrupt()方法
public static void main(String[] args) throws InterruptedException {
    AtomicBoolean isStop=new AtomicBoolean(false);
    Runnable runnable=()->{
        String path = "D:\\hello.txt";
        try {
            FileOutputStream outputStream = new FileOutputStream(path);
            int count=0;
            while(true){
                outputStream.write(count++);
                if(isStop.get()){
                    break;
                }
            }
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    };
    Thread t1 = new Thread(runnable);
    t1.start();
    Thread.sleep(1000);
    t1.interrupt();
    Thread.sleep(1000);
    System.out.println("完成");
}
//这里程序一直没有结束运行判断线程t1还在执行

回收线程

processWorkerExit()
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    /**
     * 1、worker数量-1
     * 如果是突然终止,说明是task执行时异常情况导致,即run()方法执行时发生了异常,那么正在工作的worker线程数量需要-1
     * 如果不是突然终止,说明是worker线程没有task可执行了,不用-1,因为已经在getTask()方法中-1了
     */
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted 代码和注释相反
        decrementWorkerCount();
    /**
     * 2、从Workers Set中移除worker
     */
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        //把worker的完成任务数加到线程池的完成任务数
        completedTaskCount += w.completedTasks;
        //从HashSet<Worker>中移除
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
    /**
     * 3、在对线程池有负效益的操作时,都需要“尝试终止”线程池
     * 主要是判断线程池是否满足终止的状态
     * 如果状态满足,但还有线程池还有线程,尝试对其发出中断响应,使其能进入退出流程
     * 没有线程了,更新状态为tidying->terminated
     */
    tryTerminate();
    /**
     * 4、是否需要增加worker线程
     * 线程池状态是running 或 shutdown
     * 如果当前线程是突然终止的,addWorker()
     * 如果当前线程不是突然终止的,但当前线程数量 < 要维护的线程数量,addWorker()
     * 故如果调用线程池shutdown(),直到workQueue为空前,线程池都会维持corePoolSize个线程,然后再逐渐销毁这corePoolSize个线程
     */
    int c = ctl.get();
    //如果状态是running、shutdown,即tryTerminate()没有成功终止线程池,尝试再添加一个worker
    if (runStateLessThan(c, STOP)) {
        //不是突然完成的,即没有task任务可以获取而完成的,计算min,并根据当前worker数量判断是否需要addWorker()
        if (!completedAbruptly) {
            //allowCoreThreadTimeOut默认为false,即min默认为corePoolSize
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            //如果min为0,即不需要维持核心线程数量,且workQueue不为空,至少保持一个线程
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            //如果线程数量大于最少数量,直接返回,否则下面至少要addWorker一个
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        //添加一个没有firstTask的worker
        //只要worker是completedAbruptly突然终止的,或者线程数量小于要维护的数量,就新添一个worker线程,即使是shutdown状态
        addWorker(null, false);
    }
}

将线程引用移出线程池就已经结束了线程销毁的部分。但由于引起线程销毁的可能性有很多,线程池还要判断是什么引发了这次销毁,是否要改变线程池的现阶段状态,是否要根据新状态,重新分配线程。

阻塞队列

img

遗留问题

为什么使用ReentrantLock

Synchronized和volatile使用场景

worker实现了AbstractQueuedSynchronizer接口

参考:

  1. Java线程池实现原理及其在美团业务中的实践
  2. Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理
posted @ 2021-03-26 14:22  froggengo  阅读(88)  评论(0编辑  收藏  举报