很久前(2020-10-23),就有想法学习线程池并输出博客,但是写着写着感觉看不懂了,就不了了之了。现在重拾起,重新写一下(学习一下)。

线程池的优点也是老生常谈的东西了

  1. 减少线程创建的开销(任务数大于线程数时)
  2. 统一管理一系列的线程(资源)

在讲ThreadPoolExecutor前,我们先看看它的父类都有些啥。

Executor,执行提交的Runnable任务的对象,将任务提交与何时执行分离开。

execute方法是Executor接口的唯一方法。

  1. // 任务会在未来某时执行,可能执行在一个新线程中、线程池或调用该任务的线程中。
  2. void execute(Runnable command);

ExecutorService是一个Executor,提供了管理终止的方法和返回Future来跟踪异步任务的方法(sumbit)。

终止的两个方法

  • shutdown(), 正在执行的任务继续执行,不接受新任务
  • shutdownNow(), 正在执行的任务也要被终止

AbstractExecutorService,实现了ExecutorServicesumbitinvokeAny,invokeAll

介绍

线程池主要元素

底层变量

ctl

我们讲讲先ctl(The main pool control state), 其包含两个信息

  1. 线程池的状态(最高三位)
  2. 线程池的workerCount,有效的线程数
  1. private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  2. private static final int COUNT_BITS = Integer.SIZE - 3;
  3. private static final int CAPACITY = (1 << COUNT_BITS) - 1;
  4. // runState is stored in the high-order bits
  5. private static final int RUNNING = -1 << COUNT_BITS;
  6. private static final int SHUTDOWN = 0 << COUNT_BITS;
  7. private static final int STOP = 1 << COUNT_BITS;
  8. private static final int TIDYING = 2 << COUNT_BITS;
  9. private static final int TERMINATED = 3 << COUNT_BITS;

‍线程池的状态转化图

看一下每个状态的含义

  1. RUNNING, 接受新的任务并且处理阻塞队列的任务
  2. SHUTDOWN, 拒绝新任务,但是处理阻塞队列的任务
  3. STOP, 拒绝新任务,并且抛弃阻塞队列中的任务,还要中断正在运行的任务
  4. TIDYING,所有任务执行完(包括阻塞队列中的任务)后, 当前线程池活动线程为0, 将要调用terminated方法
  5. TERMINATED, 终止状态。调用terminated方法后的状态

workers

工作线程都添加到这个集合中。可以想象成一个集中管理的平台,可以通过workers获取活跃的线程数,中断所有线程等操作。

  1. private final HashSet<Worker> workers = new HashSet<Worker>();

可修改变量

构造器中的参数

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime, // 最大等待任务的时间,超过则终止超过corePoolSize的线程
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue, // 阻塞队列
  6. ThreadFactory threadFactory, // executor使用threadFactory创建一个线程
  7. RejectedExecutionHandler handler) // 拒绝策略

corePoolSize、maximumPoolSize,workQueue三者的关系:

  • 当线程数小于corePoolSize,任务进入,即使有其他线程空闲,也会创建一个新的线程
  • 大于corePoolSize且小于maximumPoolSizeworkQueue未满,将任务加入到workQueue中;只有workQueue满了,才会新建一个线程
  • workQueue已满,且任务大于maximumPoolSize,将会采取拒绝策略(handler)

拒绝策略:

  1. AbortPolicy, 直接抛出RejectedExecutionException
  2. CallerRunsPolicy, 使用调用者所在线程来执行任务
  3. DiscardPolicy, 默默丢弃
  4. DiscardOldestPolicy, 丢弃头部的一个任务,重试

allowCoreThreadTimeOut

控制空闲时,core threads是否被清除。

探索源码

最重要的方法就是execute

提交的任务将在未来某个时候执行

  1. public void execute(Runnable command) {
  2. if (command == null)
  3. throw new NullPointerException();
  4. /*
  5. * Proceed in 3 steps:
  6. *
  7. * 1. If fewer than corePoolSize threads are running, try to
  8. * start a new thread with the given command as its first
  9. * task. The call to addWorker atomically checks runState and
  10. * workerCount, and so prevents false alarms that would add
  11. * threads when it shouldn't, by returning false.
  12. *
  13. * 2. If a task can be successfully queued, then we still need
  14. * to double-check whether we should have added a thread
  15. * (because existing ones died since last checking) or that
  16. * the pool shut down since entry into this method. So we
  17. * recheck state and if necessary roll back the enqueuing if
  18. * stopped, or start a new thread if there are none.
  19. *
  20. * 3. If we cannot queue task, then we try to add a new
  21. * thread. If it fails, we know we are shut down or saturated
  22. * and so reject the task.
  23. */
  24. // 获取workCount与runState
  25. int c = ctl.get();
  26. if (workerCountOf(c) < corePoolSize) {
  27. if (addWorker(command, true))
  28. return;
  29. c = ctl.get();
  30. }
  31. if (isRunning(c) && workQueue.offer(command)) {
  32. int recheck = ctl.get();
  33. if (! isRunning(recheck) && remove(command))
  34. reject(command);
  35. else if (workerCountOf(recheck) == 0)
  36. addWorker(null, false);
  37. }
  38. else if (!addWorker(command, false))
  39. reject(command);
  40. }

根据上面的注释,我们将execute分为三个部分来讲解

  • 当正在运行的线程数小于corePoolSize
  • 当大于corePoolSize时,需要入队
  • 队列已满

当正在运行的线程数小于corePoolSize

  1. // execute第一部分代码
  2. if (workerCountOf(c) < corePoolSize) {
  3. if (addWorker(command, true))
  4. return;
  5. c = ctl.get();
  6. }

addWorker, 创建工作线程。

当然它不会直接就添加一个新的工作线程,会检测runStateworkCount,来避免不必要的新增。检查没问题的话,新建线程,将其加入到wokers,并将线程启动。

  1. // firstTask,当线程启动时,第一个任务
  2. // core,为true就是corePoolSize作为边界,反之就是maximumPoolSize
  3. private boolean addWorker(Runnable firstTask, boolean core) {
  4. retry:
  5. for (;;) {
  6. int c = ctl.get();
  7. int rs = runStateOf(c);
  8. // Check if queue empty only if necessary.
  9. if (rs >= SHUTDOWN &&
  10. ! (rs == SHUTDOWN &&
  11. firstTask == null &&
  12. ! workQueue.isEmpty()))
  13. return false;
  14. for (;;) {
  15. int wc = workerCountOf(c);
  16. if (wc >= CAPACITY ||
  17. wc >= (core ? corePoolSize : maximumPoolSize))
  18. return false;
  19. if (compareAndIncrementWorkerCount(c))
  20. break retry;
  21. c = ctl.get(); // Re-read ctl
  22. if (runStateOf(c) != rs)
  23. continue retry;
  24. // else CAS failed due to workerCount change; retry inner loop
  25. }
  26. }
  27. boolean workerStarted = false;
  28. boolean workerAdded = false;
  29. Worker w = null;
  30. try {
  31. w = new Worker(firstTask);
  32. final Thread t = w.thread;
  33. if (t != null) {
  34. final ReentrantLock mainLock = this.mainLock;
  35. mainLock.lock();
  36. try {
  37. // Recheck while holding lock.
  38. // Back out on ThreadFactory failure or if
  39. // shut down before lock acquired.
  40. int rs = runStateOf(ctl.get());
  41. if (rs < SHUTDOWN ||
  42. (rs == SHUTDOWN && firstTask == null)) {
  43. if (t.isAlive()) // precheck that t is startable
  44. throw new IllegalThreadStateException();
  45. workers.add(w);
  46. int s = workers.size();
  47. if (s > largestPoolSize)
  48. largestPoolSize = s;
  49. workerAdded = true;
  50. }
  51. } finally {
  52. mainLock.unlock();
  53. }
  54. if (workerAdded) {
  55. t.start();
  56. workerStarted = true;
  57. }
  58. }
  59. } finally {
  60. if (! workerStarted)
  61. addWorkerFailed(w);
  62. }
  63. return workerStarted;
  64. }

上面的代码很长,我们将它分为两部分

  1. // addWorker()第一部分代码
  2. // 这部分主要是通过CAS增加workerCount
  3. retry:
  4. for (;;) {
  5. int c = ctl.get();
  6. int rs = runStateOf(c);
  7. // Check if queue empty only if necessary.
  8. // 下面的条件我们将它转化成
  9. // if (rs >= SHUTDOWN &&
  10. // (rs != SHUTDOWN ||
  11. // firstTask != null ||
  12. // workQueue.isEmpty()))
  13. // 结合线程池状态分析!
  14. // 情况1. 当前的线程池状态为STOP、TIDYING,TERMINATED
  15. // 情况2. 当前线程池的状态为SHUTDOWN且firstTask不为空,只有RUNNING状态才可以接受新任务
  16. // 情况3. 当前线程池的状态为SHUTDOWN且firstTask为空且队列为空。
  17. // 这几种情况,没有必要新建worker(线程)。
  18. if (rs >= SHUTDOWN &&
  19. ! (rs == SHUTDOWN &&
  20. firstTask == null &&
  21. ! workQueue.isEmpty()))
  22. return false;
  23. for (;;) {
  24. int wc = workerCountOf(c);
  25. if (wc >= CAPACITY ||
  26. wc >= (core ? corePoolSize : maximumPoolSize))
  27. return false;
  28. // CAS增加workerCount成功,继续第二部分操作
  29. if (compareAndIncrementWorkerCount(c))
  30. break retry;
  31. c = ctl.get(); // Re-read ctl
  32. // 这里的线程池状态被改变了,继续外部循环,再次检查线程池状态
  33. if (runStateOf(c) != rs)
  34. continue retry;
  35. // else CAS failed due to workerCount change; retry inner loop
  36. }
  37. }

经过上面的代码,我们成功通过CAS使workerCount + 1,下面我们就会新建worker并添加到workers中,并启动通过threadFactory创建的线程。

  1. // addWorker()第二部分代码
  2. boolean workerStarted = false;
  3. boolean workerAdded = false;
  4. Worker w = null;
  5. try {
  6. w = new Worker(firstTask);
  7. final Thread t = w.thread;
  8. if (t != null) {
  9. final ReentrantLock mainLock = this.mainLock;
  10. mainLock.lock();
  11. try {
  12. // Recheck while holding lock.
  13. // Back out on ThreadFactory failure or if
  14. // shut down before lock acquired.
  15. int rs = runStateOf(ctl.get());
  16. // 第一种情况,rs为RUNNING
  17. // 第二种情况是rs为SHUTDOWN,firstTask为null, 但是workQueue(阻塞队列)不为null,创建线程进行处理
  18. if (rs < SHUTDOWN ||
  19. (rs == SHUTDOWN && firstTask == null)) {
  20. // 这里是该线程已经被启动了,我觉得的原因是threadFactory创建了两个相同的thread,不知道还有其他原因没。
  21. if (t.isAlive()) // precheck that t is startable
  22. throw new IllegalThreadStateException();
  23. workers.add(w);
  24. int s = workers.size();
  25. if (s > largestPoolSize)
  26. largestPoolSize = s;
  27. workerAdded = true;
  28. }
  29. } finally {
  30. mainLock.unlock();
  31. }
  32. if (workerAdded) {
  33. t.start();
  34. workerStarted = true;
  35. }
  36. }
  37. } finally {
  38. // 上面的线程创建可能失败,或者线程工厂返回null
  39. // 或者线程启动时,抛出OutOfMemoryError
  40. if (! workerStarted)
  41. // 回滚状态
  42. addWorkerFailed(w);
  43. }
  44. return workerStarted;

看完了addWorker的步骤,代码中有个Worker类,看似是线程但又不完全是线程,我们去看看它的结构。

Worker

这个类的主要作用是,维护线程运行任务的中断控制状态记录每个线程完成的任务数

整体结构

  1. private final class Worker
  2. extends AbstractQueuedSynchronizer
  3. implements Runnable{
  4. /** Thread this worker is running in. Null if factory fails. */
  5. final Thread thread;
  6. /** Initial task to run. Possibly null. */
  7. Runnable firstTask;
  8. /** 每个线程的任务完成数 */
  9. volatile long completedTasks;
  10. Worker(Runnable firstTask) {
  11. setState(-1); // inhibit interrupts until runWorker
  12. this.firstTask = firstTask;
  13. // 在创建线程时,将任务传入到threadFactory中
  14. this.thread = getThreadFactory().newThread(this);
  15. }
  16. public void run() {
  17. // 将运行委托给外部方法runWorker,下面会详见。
  18. // 这里是运行任务的核心代码
  19. runWorker(this);
  20. }
  21. // 实现AQS的独占模式的方法,该锁不能重入。
  22. // Lock methods
  23. //
  24. // The value 0 represents the unlocked state.
  25. // The value 1 represents the locked state.
  26. protected boolean isHeldExclusively() {
  27. return getState() != 0;
  28. }
  29. protected boolean tryAcquire(int unused) {
  30. if (compareAndSetState(0, 1)) {
  31. setExclusiveOwnerThread(Thread.currentThread());
  32. return true;
  33. }
  34. return false;
  35. }
  36. protected boolean tryRelease(int unused) {
  37. setExclusiveOwnerThread(null);
  38. setState(0);
  39. return true;
  40. }
  41. public void lock() { acquire(1); }
  42. public boolean tryLock() { return tryAcquire(1); }
  43. public void unlock() { release(1); }
  44. public boolean isLocked() { return isHeldExclusively(); }
  45. // 线程运行之后才可以被中断
  46. void interruptIfStarted() {
  47. Thread t;
  48. if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
  49. try {
  50. t.interrupt();
  51. } catch (SecurityException ignore) {}
  52. }
  53. }
  54. }

我们来看看runWorker的实现。这个类主要的工作就是,不停地阻塞队列中获取任务并执行,若firstTask不为空,就直接执行它。

  1. final void runWorker(Worker w) {
  2. Thread wt = Thread.currentThread();
  3. Runnable task = w.firstTask;
  4. w.firstTask = null;
  5. w.unlock(); // allow interrupts
  6. boolean completedAbruptly = true;
  7. try {
  8. // getTask控制阻塞等待任务或者是否超时就清除空闲的线程
  9. // getTask非常之重要,后面会讲到
  10. while (task != null || (task = getTask()) != null) {
  11. w.lock();
  12. // If pool is stopping, ensure thread is interrupted;
  13. // if not, ensure thread is not interrupted. This
  14. // requires a recheck in second case to deal with
  15. // shutdownNow race while clearing interrupt
  16. // 第二种情况,重新检测线程池状态,因为此时可能其他线程会调用shutdownNow
  17. if ((runStateAtLeast(ctl.get(), STOP) ||
  18. (Thread.interrupted() &&
  19. runStateAtLeast(ctl.get(), STOP))) &&
  20. !wt.isInterrupted())
  21. wt.interrupt();
  22. try {
  23. // 执行前
  24. beforeExecute(wt, task);
  25. Throwable thrown = null;
  26. try {
  27. // 执行firstTask的run方法
  28. task.run();
  29. } catch (RuntimeException x) {
  30. thrown = x; throw x;
  31. } catch (Error x) {
  32. thrown = x; throw x;
  33. } catch (Throwable x) {
  34. thrown = x; throw new Error(x);
  35. } finally {
  36. // 执行后
  37. afterExecute(task, thrown);
  38. }
  39. } finally {
  40. task = null;
  41. w.completedTasks++;
  42. w.unlock();
  43. }
  44. }
  45. completedAbruptly = false;
  46. } finally {
  47. // 处理worker退出
  48. processWorkerExit(w, completedAbruptly);
  49. }
  50. }

‍ 启动一个线程,大致执行的方法流程

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:

  1. There are more than maximumPoolSize workers (due to a call to setMaximumPoolSize).
  2. The pool is stopped.
  3. The pool is shutdown and the queue is empty.
  4. This worker timed out waiting for a task, and timed-out workers are subject to termination (that is, allowCoreThreadTimeOut || workerCount > corePoolSize) both before and after the timed wait, and if the queue is non-empty, this worker is not the last thread in the pool. ‍(超时等待任务的worker,在定时等待前后都会被终止(情况有,allowCoreThreadTimeOut || wc > corePoolSize)

Returns:

task, or null if the worker must exit, in which case workerCount is decremented(worker退出时,workerCount会减一)

  1. private Runnable getTask() {
  2. boolean timedOut = false; // Did the last poll() time out?
  3. for (;;) {
  4. int c = ctl.get();
  5. int rs = runStateOf(c);
  6. // 检测是否有必要返回新任务,注意每个状态的含义就明白了
  7. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
  8. decrementWorkerCount();
  9. return null;
  10. }
  11. int wc = workerCountOf(c);
  12. // 检测worker是否需要被淘汰
  13. boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
  14. // 下面的代码结合上面的timed变量,超时后,当大于corePoolSize时,返回null
  15. // 或者当allowCoreThreadTimeOut = true时,超时后,返回null
  16. if ((wc > maximumPoolSize || (timed && timedOut))
  17. && (wc > 1 || workQueue.isEmpty())) {
  18. if (compareAndDecrementWorkerCount(c))
  19. return null;
  20. continue;
  21. }
  22. try {
  23. Runnable r = timed ?
  24. workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
  25. workQueue.take();
  26. // 这里r为null的话,只能是timed = true的情况;take(),一直会阻塞直到有任务返回
  27. if (r != null)
  28. return r;
  29. timedOut = true;
  30. } catch (InterruptedException retry) {
  31. timedOut = false;
  32. }
  33. }
  34. }

我们来看看当getTask返回null时,线程池是如何处理worker退出的

根据runWorker的代码,getTask为null,循环体正常退出,此时completedAbruptly = false;

processWorkerExit

  1. private void processWorkerExit(Worker w, boolean completedAbruptly) {
  2. // 1. 有异常退出的话, workerCount将会减一
  3. // 2. 正常退出的话,因为在getTask中已经减一,所以这里不用理会
  4. if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
  5. decrementWorkerCount();
  6. // 将worker完成的任务数加到completedTaskCount
  7. // 从workers中移除当前worker
  8. final ReentrantLock mainLock = this.mainLock;
  9. mainLock.lock();
  10. try {
  11. completedTaskCount += w.completedTasks;
  12. workers.remove(w);
  13. } finally {
  14. mainLock.unlock();
  15. }
  16. // 检测线程池是否有资格设置状态为TERMINATED
  17. tryTerminate();
  18. int c = ctl.get();
  19. // 此时的状态是RUNNING或SHUTDOWN
  20. if (runStateLessThan(c, STOP)) {
  21. // 1. 非正常退出的,addWorker()
  22. // 2. 正常退出的, workerCount小于最小的线程数,就addWorker()
  23. if (!completedAbruptly) {
  24. int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
  25. if (min == 0 && ! workQueue.isEmpty())
  26. min = 1;
  27. if (workerCountOf(c) >= min)
  28. return; // replacement not needed
  29. }
  30. addWorker(null, false);
  31. }
  32. }

getTask是保证存在的线程不被销毁的核心,getTask则利用阻塞队列take方法,一直阻塞直到获取到任务为止。

当大于corePoolSize时,需要入队

  1. // execute第二部分代码
  2. // 线程池状态是RUNNING(只有RUNNING才可以接受新任务)
  3. // 此时,workerCount >= corePoolSize, 将任务入队
  4. if (isRunning(c) && workQueue.offer(command)) {
  5. int recheck = ctl.get();
  6. // 此时线程池可能被shutdown了。
  7. // 需要清除刚添加的任务,若任务还没有被执行,就可以让它不被执行
  8. if (! isRunning(recheck) && remove(command))
  9. reject(command);
  10. // 若此时没有worker,新建一个worker去处理队列中的任务
  11. else if (workerCountOf(recheck) == 0)
  12. addWorker(null, false);
  13. }

队列已满

  1. // execute第三部分代码
  2. // addWorker第二个参数false表明,以maximumPoolSize为界限
  3. else if (!addWorker(command, false))
  4. // workerCount > maximumPoolSize 就对任务执行拒绝策略
  5. reject(command);

我们就讲完了执行方法execute(),有兴趣的同学可以去看看关闭方法shutdown()以及shutdownNow(),看看他们的区别。当然也可以去研究一下其他方法的源码。

探究一些小问题

  1. runWorker为啥这样抛错
  1. try {
  2. task.run();
  3. } catch (RuntimeException x) {
  4. thrown = x; throw x;
  5. } catch (Error x) {
  6. thrown = x; throw x;
  7. } catch (Throwable x) {
  8. thrown = x; throw new Error(x);
  9. } finally {
  10. ...
  11. }

We separately handle RuntimeException, Error (both of which the specs guarantee that we trap) and arbitrary Throwables. Because we cannot rethrow Throwables within Runnable.run, we wrap them within Errors on the way out (to the thread's UncaughtExceptionHandler). Any thrown exception also conservatively causes thread to die.

大致意思就是,分别处理RuntimeException、Error和任何的Throwable。因为不能在 Runnable.run 中重新抛出 Throwables,所以将它们包装在 Errors中(到线程的 UncaughtExceptionHandler).

Runnable.run不能抛出Throwables的原因是,Runnable中的run并没有定义抛出任何异常,继承它的子类,抛错的范围不能超过父类

UncaughtExceptionHandler可以处理“逃逸的异常”,可以去了解一下。

  1. 创建线程池最好手动创建,参数根据系统自定义



    图中的设置线程数的策略只是初步设置,下一篇我们去研究具体的线程数调优

  2. 为什么创建线程开销大

    启动一个线程时,将涉及大量的工作

  • 必须为线程堆栈分配和初始化一大块内存。
  • 需要创建/注册native thread在host OS中
  • 需要创建、初始化描述符并将其添加到 JVM 内部数据结构中。

虽然启动一个线程的时间不长,耗费的资源也不大,但有个东西叫"积少成多"。就像

Doug Lea写的源码一样,有些地方的细节优化,看似没必要,但是请求一多起来,那些细节就是"点睛之笔"了。

当我们有大量需要线程时且每个任务都是独立的,尽量考虑使用线程池

总结

线程池的总体流程图

线程池新建线程,如何保证可以不断地获取任务,就是通过阻塞队列(BlockingQueue)的take方法,阻塞自己直到有任务才返回。

本篇博客也到这里就结束了,学习线程池以及输出博客,中间也拖了很久,最后送给大家以及自己最近看到的一句话

往往最难的事和自己最应该做的事是同一件事

参考

JAVA并发(8)-ThreadPoolExecutor的讲解的更多相关文章

  1. java并发初探ThreadPoolExecutor拒绝策略

    java并发初探ThreadPoolExecutor拒绝策略 ThreadPoolExecuter构造器 corePoolSize是核心线程池,就是常驻线程池数量: maximumPoolSize是最 ...

  2. Java并发编程--ThreadPoolExecutor

    概述 为什么要使用线程池? 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立 ...

  3. 【Java 并发】详解 ThreadPoolExecutor

    前言 线程池是并发中一项常用的优化方法,通过对线程复用,减少线程的创建,降低资源消耗,提高程序响应速度.在 Java 中我们一般通过 Exectuors 提供的工厂方法来创建线程池,但是线程池的最终实 ...

  4. java并发线程池---了解ThreadPoolExecutor就够了

    总结:线程池的特点是,在线程的数量=corePoolSize后,仅任务队列满了之后,才会从任务队列中取出一个任务,然后构造一个新的线程,循环往复直到线程数量达到maximumPoolSize执行拒绝策 ...

  5. Java并发——ThreadPoolExecutor线程池解析及Executor创建线程常见四种方式

    前言: 在刚学Java并发的时候基本上第一个demo都会写new Thread来创建线程.但是随着学的深入之后发现基本上都是使用线程池来直接获取线程.那么为什么会有这样的情况发生呢? new Thre ...

  6. Java并发编程:ThreadPoolExecutor + Callable + Future(FutureTask) 探知线程的执行状况

    如题 (总结要点) 使用ThreadPoolExecutor来创建线程,使用Callable + Future 来执行并探知线程执行情况: V get (long timeout, TimeUnit ...

  7. Java并发必知必会第三弹:用积木讲解ABA原理

    Java并发必知必会第三弹:用积木讲解ABA原理 可落地的 Spring Cloud项目:PassJava 本篇主要内容如下 一.背景 上一节我们讲了程序员深夜惨遭老婆鄙视,原因竟是CAS原理太简单? ...

  8. Java并发机制(7)--线程池ThreadPoolExecutor的使用

    Java并发编程:线程池的使用整理自:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3932921.html 1.什么是线程池,为什么要使用线程池: 1.1. ...

  9. Java并发编程:线程池的使用

    Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, ...

随机推荐

  1. gitbook安装使用教程

    以下是gitbook的简略安装使用过程,可以参考一下.后续有时间我再回头修改完善实验目的:安装gitbook后,将相关的文件发布到gitlab上安装node.js在cmd下执行安装npm instal ...

  2. debian用户手册-20200317

    https://www.debian.org/doc/manuals/debian-reference/ 文档与使用手册在每一个操作系统中都是很重要的一部份,是描述程序操作和使用的技术手册.正由于说明 ...

  3. 11.14 mii-tool:管理网络接口的状态

    mii-tool命令用于查看.管理网络接口,默认情况下网卡的状态是自动协商的,但是有时也会出现不正常的情况,可以使用mii-tool进行调整. mii-tool [option] [interface ...

  4. Prometheus存储原理及数据备份还原

    prometheus将采集到的样本以时间序列的方式保存在内存(TSDB 时序数据库)中,并定时保存到硬盘中.与zabbix不同,zabbix会保存所有的数据,而prometheus本地存储会保存15天 ...

  5. 9.7 top:实时显示系统中各个进程的资源占用状况

    top命令 用于实时地对系统处理器状态进行监控,它能够实时地显示系统中各个进程的资源占用状况.该命令可以按照CPU的使用.内存的使用和执行时间对系统任务进程进行排序显示,同时top命令还可以通过交互式 ...

  6. 2.2 CPU 上下文切换是什么意思?(下)

    怎么查看系统的上下文切换情况 过多的上下文切换,会把 CPU 时间消耗在寄存器.内核栈以及虚拟内存等数据的保存和恢复上,缩短进程真正运行的时间,成了系统性能大幅下降的一个 元凶. 使用 vmstat ...

  7. 大师画PCB板子

    1.低频电路对于模拟地和数字地要分开布线,不能混用 2.如果有多个A/D转换电路,几个ADC尽量放在一起,只在尽量靠近该器件处单点接地,AGND和DGND都要接到模拟地,电源端子都要接到模拟电源端子: ...

  8. 链路追踪_SkyWalking的部署及使用

    关于链路追踪,目前比较主流是Cat,Zipkin,SkyWalking等这些工具.这篇文章主要介绍关于SkyWalking工具的. 为什么用SkyWalking,因为它基本没有代码侵入,只这一点就足够 ...

  9. Spring5.0源码学习系列之事务管理概述

    Spring5.0源码学习系列之事务管理概述(十一),在学习事务管理的源码之前,需要对事务的基本理论比较熟悉,所以本章节会对事务管理的基本理论进行描述 1.什么是事务? 事务就是一组原子性的SQL操作 ...

  10. MegEngine计算图、MatMul优化解析

    MegEngine计算图.MatMul优化解析 本文针对天元在推理优化过程中所涉及的计算图优化与 MatMul 优化进行深度解读,希望能够帮助广大开发者在利用天元 MegEngine「深度学习,简单开 ...