线程池ThreadPoolExecutor、Executors参数详解与源代码分析

MinIO vs 华为云OBS:成本、性能与兼容性深度对比(含实测数据) 本文深度对比了开源对象存储MinIO与华为云OBS在成本、性能、兼容性及运维方面的核心差异。通过实测数据与全生命周期成本分析,为技术决策者提供选型依据。文章指出,MinIO在长期成本控制、性能深度优化及S3 API兼容性方面优势显著,而华为云OBS则提供开箱即用的便利性。 阅读详情

线程池ThreadPoolExecutor、Executors参数详解与源代码分析
ThreadPoolExecutor是核心
Executors是一个方便使用的线程池创建工具

为了防止使用者错误搭配ThreadPoolExecutor构造函数的各个参数以及更加方便简洁的创建ThreadPoolExecutor对象,JavaSE中又定义了Executors类,Eexcutors类提供了创建常用配置线程池的方法。

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

ThreadPoolExecutor

Private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING,0));

ctl主要用于存储线程池的工作状态以及池中正在运行的线程数。显然要在一个整型变量存储两个数据,只能将其一分为二。其中高3bit用于存储线程池的状态,低位的29bit用于存储正在运行的线程数。

线程池具有以下五种状态,当创建一个线程池时初始化状态为RUNNING

  1. RUNNING 允许提交并处理任务
  2. SHUTDOWN 不允许提交新的任务,但是会处理完已提交的任务
  3. STOP 不允许提交新的任务,也不会处理阻塞队列中未执行的任务,并设置正在执行的线程的中断标志位
  4. TIDYING 所有任务执行完毕,池中工作的线程数为0,等待执行terminated()勾子方法
  5. TERMINATED terminated()勾子方法执行完毕

注意:

这里说的是线程池的状态而不是池中线程的状态。
调用线程池的shutdown方法,将线程池由RUNNING(运行状态)转换为SHUTDOWN状态。
调用线程池的shutdownNow方法,将线程池由RUNNING或SHUTDOWN状态转换为STOP状态。
SHUTDOWN状态和STOP状态先会转变为TIDYING状态,最终都会变为TERMINATED

Private static int runStateOf(int c)
Private static int workerCountOf(int c)
Private static int ctlOf(int rs,int wc)

ThreadPoolExecutor同时提供上述三个方法用于池中的线程查看线程池的状态和计算正在运行的线程数。

Private int largestPoolSize;
Private final BlockingQueue<Runnable>workQueue;
Private volatile long keepAliveTime;
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private volatile ThreadFactory threadFactory;
private volatile RejectedExecutionHandler handler;

上述数据成员对线程池的性能也有很大的影响,我会将它们放到构造中讲解。

Privatefinal HashSet<Worker> workers= new HashSet<Worker>();
Privatelong completedTaskCount;
Private volatile boolean allowCoreThreadTimeOut;
private int largestPoolSize;
  • completedTaskCount表示线程池已完成的任务数。
  • allowCoreThreadTimeeOut表示是否允许核心线程在空闲状态下自行销毁。
  • largestPoolSize 表示线程池从创建到现在,池中线程的最大数量
private final HashSet<Worker> workers = new HashSet<Worker>();

workers是个HashSet容器,它存储的是Worker类的对象,Worker是线程池的内部类,它继承了Runnable接口,不严格的情况下,可以将一个Worker对象看成Thread对象,也就是工作的线程。shutdown和shutdownNow方法中会使用workers完成对所有线程的遍历。

Privatefinal ReentrantLock mainLock =new ReentrantLock();
Privatefinal Condition termination = mainLock.newCondition();

mainLock主要用于同步访问(或者说改变)线程池的状态以及线程池的各项参数,比如completedTaskCount和workers等。
在awaitTermination方法中,(mianLock的)termination是用于延时的条件队列。

构造函数及关键参数

publicThreadPoolExecutor(intcorePoolSize,
 int maximumPoolSize,
 long keepAliveTime,
 TimeUnit unit,
 BlockingQueue<Runnable> workQueue,
 ThreadFactory threadFactory,
 RejectedExecutionHandler handler)

线程池的构造函数参数多达7个(选择重要的五个记住——阿里面试会问到),现在我们一一来分析它们对线程池的影响。

  • corePoolSize:线程池中核心线程数的最大值

  • maximumPoolSize:线程池中能拥有最多线程数

  • workQueue:用于缓存任务的阻塞队列

    我们现在通过向线程池添加新的任务来说明着三者之间的关系。
    (1)如果没有空闲的线程执行该任务且当前运行的线程数少于corePoolSize,则添加新的线程执行该任务。
    (2)如果没有空闲的线程执行该任务且当前的线程数等于corePoolSize同时阻塞队列未满,则将任务入队列,而不添加新的线程。
    (3)如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数小于maximumPoolSize,则创建新的线程执行任务。
    (4)如果没有空闲的线程执行该任务且阻塞队列已满同时池中的线程数等于maximumPoolSize,则根据构造函数中的handler指定的策略来拒绝新的任务。
    注意,线程池并没有标记哪个线程是核心线程,哪个是非核心线程,线程池只关心核心线程的数量。
    通俗解释,如果把线程池比作一个单位的话,corePoolSize就表示正式工,线程就可以表示一个员工。当我们向单位委派一项工作时,如果单位发现正式工还没招满,单位就会招个正式工来完成这项工作。随着我们向这个单位委派的工作增多,即使正式工全部满了,工作还是干不完,那么单位只能按照我们新委派的工作按先后顺序将它们找个地方搁置起来,这个地方就是workQueue,等正式工完成了手上的工作,就到这里来取新的任务。如果不巧,年末了,各个部门都向这个单位委派任务,导致workQueue已经没有空位置放新的任务,于是单位决定招点临时工吧(临时工:又是我!)。临时工也不是想招多少就找多少,上级部门通过这个单位的maximumPoolSize确定了你这个单位的人数的最大值,换句话说最多招maximumPoolSize–corePoolSize个临时工。当然,在线程池中,谁是正式工,谁是临时工是没有区别,完全同工同酬。

  • keepAliveTime:表示空闲线程的存活时间。

  • TimeUnitunit:表示keepAliveTime的单位。

    为了解释keepAliveTime的作用,我们在上述情况下做一种假设。假设线程池这个单位已经招了些临时工,但新任务没有继续增加,所以随着每个员工忙完手头的工作,都来workQueue领取新的任务(看看这个单位的员工多自觉啊)。随着各个员工齐心协力,任务越来越少,员工数没变,那么就必定有闲着没事干的员工。这样的话领导不乐意啦,但是又不能轻易fire没事干的员工,因为随时可能有新任务来,于是领导想了个办法,设定了keepAliveTime,当空闲的员工在keepAliveTime这段时间还没有找到事情干,就被辞退啦,毕竟地主家也没有余粮啊!当然辞退到corePoolSize个员工时就不再辞退了,领导也不想当光杆司令啊!

  • handler:表示当workQueue已满,且池中的线程数达到maximumPoolSize时,线程池拒绝添加新任务时采取的策略。

为了解释handler的作用,我们在上述情况下做另一种假设。假设线程池这个单位招满临时工,但新任务依然继续增加,线程池从上到下,从里到外真心忙的不可开交,阻塞队列也满了,只好拒绝上级委派下来的任务。怎么拒绝是门艺术,handler一般可以采取以下四种取值。

  • ThreadPoolExecutor.AbortPolicy() -
  • 抛出RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy()由向线程池提交任务的线程来执行该任务
ThreadPoolExecutor.DiscardOldestPolicy()抛弃最旧的任务(最先提交而没有得到执行的任务)
ThreadPoolExecutor.DiscardPolicy()抛弃当前的任务
  • workQueue:它决定了缓存任务的排队策略。对于不同的应用场景我们可能会采取不同的排队策略,这就需要不同类型的阻塞队列,在线程池中常用的阻塞队列有以下2种:

(1)SynchronousQueue < Runnable >:此队列中不缓存任何一个任务。向线程池提交任务时,如果没有空闲线程来运行任务,则入列操作会阻塞。当有线程来获取任务时,出列操作会唤醒执行入列操作的线程。从这个特性来看,SynchronousQueue是一个无界队列,因此当使用SynchronousQueue作为线程池的阻塞队列时,参数maximumPoolSizes没有任何作用。
(2)LinkedBlockingQueue < Runnable >:顾名思义是用链表实现的队列,可以是有界的,也可以是无界的,但在Executors中默认使用无界的。

  • threadFactory:指定创建线程的工厂

实际上ThreadPoolExecutor类中还有很多重载的构造函数,下面这个构造函数在Executors中经常用到。

public ThreadPoolExecutor(int corePoolSize,
 int maximumPoolSize,
 long keepAliveTime,
 TimeUnit unit,
 BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
    Executors.defaultThreadFactory(), defaultHandler);
}

注意到上述的构造方法使用Executors中的defaultThreadFactory()线程工厂和ThreadPoolExecutor中的defaultHandler抛弃策略。

使用defaultThreadFactory创建的线程同属于相同的线程组,具有同为Thread.NORM_PRIORITY的优先级,以及名为”pool-XXX-thread-“的线程名(XXX为创建线程时顺序序号),且创建的线程都是非守护进程。

defaultHandler缺省抛弃策是ThreadPoolExecutor.AbortPolicy()。

除了在创建线程池时指定上述参数的值外,还可在线程池创建以后通过如下方法进行设置。

Public void allowCoreThreadTimeOut(boolean value)
Public void setKeepAliveTime(long time,TimeUnit unit)
Public void setMaximumPoolSize(int maximumPoolSize)
Public void setCorePoolSize(int corePoolSize)
Public void setThreadFactory(ThreadFactory threadFactory)
Public void setRejectedExecutionHandler(RejectedExecutionHandler handler)
  1. 其它有关涉及池中线程数量的相关方法
public void allowCoreThreadTimeOut(boolean value) 
public int prestartAllCoreThreads()

默认情况下,当池中有空闲线程,且线程的数量大于corePoolSize时,空闲时间超过keepAliveTime的线程会自行销毁,池中仅仅会保留corePoolSize个线程。如果线程池中调用了allowCoreThreadTimeOut这个方法,则空闲时间超过keepAliveTime的线程全部都会自行销毁,而不必理会corePoolSize这个参数。

如果池中的线程数量小于corePoolSize时,调用prestartAllCoreThreads方法,则无论是否有待执行的任务,线程池都会创建新的线程,直到池中线程数量达到corePoolSize。

Executors中的线程池的工厂方法

为了防止使用者错误搭配ThreadPoolExecutor构造函数的各个参数以及更加方便简洁的创建ThreadPoolExecutor对象,JavaSE中又定义了Executors类,Eexcutors类提供了创建常用配置线程池的方法。以下是Executors常用的三个创建线程池的源代码。

从源码中可以看出,Executors间接的调用了重载的ThreadPoolExecutor构造函数,并帮助用户根据不同的应用场景,配置不同的参数。

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
      60L, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>());
}

newCachedThreadPool:使用SynchronousQueue作为阻塞队列,队列无界,线程的空闲时限为60秒。这种类型的线程池非常适用IO密集的服务,因为IO请求具有密集、数量巨大、不持续、服务器端CPU等待IO响应时间长的特点。服务器端为了能提高CPU的使用率就应该为每个IO请求都创建一个线程,以免CPU因为等待IO响应而空闲。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
      0L, TimeUnit.MILLISECONDS,
      new LinkedBlockingQueue<Runnable>());
}

newFixedThreadPool:需指定核心线程数,核心线程数和最大线程数相同,使用LinkedBlockingQueue 作为阻塞队列,队列无界,线程空闲时间0秒。这种类型的线程池可以适用CPU密集的工作,在这种工作中CPU忙于计算而很少空闲,由于CPU能真正并发的执行的线程数是一定的(比如四核八线程),所以对于那些需要CPU进行大量计算的线程,创建的线程数超过CPU能够真正并发执行的线程数就没有太大的意义。

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
 (new ThreadPoolExecutor(1, 1,
    0L, TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<Runnable>()));
}

newSingleThreadExecutor:池中只有一个线程工作,阻塞队列无界,它能保证按照任务提交的顺序来执行任务。

任务的提交过程

submit方法源码

public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}

public <T> Future<T> submit(Callable<T> task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<T> ftask = newTaskFor(task);
    execute(ftask);
    return ftask;
}

submit的实现方法位于抽象类AbstractExecutorService中,而此时execute方法还未实现(而是在AbstractExecutorService的继承类ThreadPoolExecutor中实现)。submit有三种重载方法,这里我选取了两个常用的进行分析,可以看出无论哪个submit方法都最终调用了execute方法。

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

由于execute方法中多次调用addWorker,我们这里就简要介绍一下它,这个方法的主要作用就是创建一个线程来执行Runnnable对象。

addWorker(Runnable firstTask, boolean core)
第一个参数firstTask不为null,则创建的线程就会先执行firstTask对象,然后去阻塞队列中取任务,否直接到阻塞队列中获取任务来执行。第二个参数,core参数为真,则用corePoolSize作为池中线程数量的最大值;为假,则以maximumPoolSize作为池中线程数量的最大值。

简要分析一下execute源码,执行一个Runnable对象时,首先通过workerCountOf(c)获取线程池中线程的数量,如果池中的数量小于corePoolSize就调用addWorker添加一个线程来执行这个任务。否则通过workQueue.offer(command)方法入列。如果入列成功还需要在一次判断池中的线程数,因为我们创建线程池时可能要求核心线程数量为0,所以我们必须使用addWorker(null, false)来创建一个临时线程去阻塞队列中获取任务来执行。

isRunning(c) 的作用是判断线程池是否处于运行状态,如果入列后发现线程池已经关闭,则出列。不需要在入列前判断线程池的状态,因为判断一个线程池工作处于RUNNING状态到执行入列操作这段时间,线程池可能被其它线程关闭了,所以提前判断毫无意义。

addWorker源码

private boolean addWorker(Runnable firstTask, boolean core) {
    //这个两个for循环主要是判断能否增加一个线程,
    //外循环来判断线程池的状态
    //内循环主要是个增加线程数的CAS操作
    retry:
    for (;;) {
 int c = ctl.get();
 int rs = runStateOf(c);

 // Check if queue empty only if necessary.
 if (rs >= SHUTDOWN &&
     ! (rs == SHUTDOWN &&
 firstTask == null &&
 ! workQueue.isEmpty()))
     return false;

 for (;;) {
     int wc = workerCountOf(c);
     if (wc >= CAPACITY ||
  wc >= (core ? corePoolSize : maximumPoolSize))
  return false;
     if (compareAndIncrementWorkerCount(c))
  break retry;
     c = ctl.get();  // Re-read ctl
     if (runStateOf(c) != rs)
  continue retry;
     // 如果是因为线程数的改变导致CAS失败,只需要重复内循环
 }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
 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;
}

线程的执行过程

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

Thread的run方法实际上调用了Worker类的runWorker方法,而Worker类继承了AQS类,并实现了lock、unlock、trylock方法。但是这些方法不是真正意义上的锁,所以在代码中加锁操作和解锁操作没有成对出现。runWorker方法中获取到任务就“加锁”,完成任务后就“解锁”。也就是说在“加锁”到“解锁”的这段时间内,线程处于忙碌状态,而其它时间段,处于空闲状态。线程池就可以通过trylock方法来确定这个线程是否空闲。

getTask方法的主要作用是从阻塞队列中获取任务。

beforeExecute(wt, task)和afterExecute(task, thrown)是个钩子函数,如果我们需要在任务执行之前和任务执行以后进行一些操作,那么我们可以自定义一个继承ThreadPoolExecutor类,并覆盖这两个方法。

getTask源代码

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

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

 // Check if queue empty only if necessary.
 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
     decrementWorkerCount();
     return null;
 }

 int wc = workerCountOf(c);

 // Are workers subject to culling?
 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

 if ((wc > maximumPoolSize || (timed && timedOut))
     && (wc > 1 || workQueue.isEmpty())) {
     if (compareAndDecrementWorkerCount(c))
  return null;
     continue;
 }

 try {
     Runnable r = timed ?
  workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
  workQueue.take();
     if (r != null)
  return r;
     timedOut = true;
 } catch (InterruptedException retry) {
     timedOut = false;
 }
    }
}

可以看出如果允许线程在keepAliveTime时间内未获取到任务线程就销毁就调用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),否则会调用workQueue.take()方法(该方法即使获取不到任务就会一直阻塞下去)。而确定是否使用workQueue.poll方法只有两个条件决定,一个是当前池中的线程是否大于核心线程数量,第二个是是否允许核心线程销毁,两者其一满足就会调用该方法。

线程池的关闭过程

shutdown源码

public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
 checkShutdownAccess();
 advanceRunState(SHUTDOWN);
 interruptIdleWorkers();
 onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
 mainLock.unlock();
    }
    tryTerminate();
}
  • advanceRunState(SHUTDOWN)的作用是通过CAS操作将线程池的状态更改为SHUTDOWN状态。

  • interruptIdleWorkers是对空闲的线程进行中断,它实际上调用了重载带参数的函数interruptIdleWorkers(false)

  • onShutdown也是一个钩子函数

interruptIdleWorkers源码

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
 for (Worker w : workers) {
     Thread t = w.thread;
     if (!t.isInterrupted() && w.tryLock()) {
  try {
      t.interrupt();
  } catch (SecurityException ignore) {
  } finally {
      w.unlock();
  }
     }
     if (onlyOne)
  break;
 }
    } finally {
 mainLock.unlock();
    }
}

通过workers容器,遍历池中的线程,对每个线程进行tryLock()操作,如果成功说明线程空闲,则设置其中断标志位。而线程是否响应中断则由任务的编写者决定。

参考文章

[1] http://www.infoq.com/cn/articles/java-threadPool/

[2] http://my.oschina.net/u/1398304/blog/376827?fromerr=limo9iEj

[3] http://www.cnblogs.com/dolphin0520/p/3932921.html

[4] http://cuisuqiang.iteye.com/blog/2019372

[5] http://blog.sina.com.cn/s/blog_5eeabe8b0100v9i5.html

原文转载自
https://www.cnblogs.com/nullzx/p/5184164.html

uniapp [安卓苹果App端] - 详细实现手机蓝牙连接打印机及打印票据小票/标签/面单/热敏小票订单/快递单等通用兼容适配解决方案,Uniapp对接蓝牙打印机支持各种主流品牌及打印指令,强悍插件 uni-app,安卓Android,苹果ios,安卓app端,苹果App端,uniapp苹果app,蓝牙打印机,uniappAPP实现通过蓝牙连接打印机打印,uniapp蓝牙连接打印机,安卓蓝牙打印插件,蓝牙打印机,蓝牙ble低功率及高功率打印机小票、标签、面单打印,手机PDA蓝牙打印插件,连接蓝牙打印机,蓝牙连接,打印文本、图片、条码、二维码,兼容斑马打印机、佳博打印机,TSC打印机,免费蓝牙打印机组件,蓝牙打印条码,蓝牙打印机连接,热敏蓝牙打印机,便携式打印机,打印机搜索不到,连接失败,发送打印命令 阅读详情

相关推荐

STM32F103+LAN9252实战:从站代码迁移中的5个硬件坑与解决方案

本文分享了将EtherCAT从站代码从STM32F767迁移至STM32F103平台时,搭配LAN9252遇到的五个典型硬件问题及解决方案。核心挑战包括时钟源不匹配、FSMC时序配置、中断映射错误、片选资源不足以及Flash烧录异常。文章通过实战经验,强调了硬件差异对代码迁移的关键影响,并提供了具体的调试思路与配置方法。

p5l2m9n4o6q的博客 697

ThreadPoolExecutor参数说明

一、ThreadPoolExecutor的重要参数 1、corePoolSize:核心线程数 * 核心线程会一直存活,及时没有任务需要执行 * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理 * 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭 2、queueCapacity:任务队列容量(阻塞队列) * 当核心线程数达到最大时,新任务会放在队列中排

sinat_15946141的博客 3万+

攻击时间控制的动态逆三维制导律(源代码)

%参考文献:《攻击时间控制的动态逆三维制导律》,哈尔滨工程大学学报,2010 %编程难点:视线坐标系与参考坐标系间的转换关系 一、采用本文方法结果 二、偏航通道采用增广比例导引律

算法小站 2392

ThreadPoolExecutor 参数详解

1、 ThreadPoolExecutor 数据成员 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); ctl 主要用于存储线程池的工作状态以及池中正在运行的线程数。显然要在一个整型变量存储两个数据,只能将其一分为二。其中高3bit用于存储线程池的状态,低位的29bit用于存储正在运行的线程数。 线程...

猎人在吃肉 2万+

51雾化器实现

1. 雾化器简介 雾化器是将试液雾化的一种器械。雾化器是原子化系统的重要部件,其性能对测定的精密度和化学干扰等产生显著影响。因此要求雾化器喷雾稳定、雾滴细小、均匀和雾化效率高。雾化器的类型有超声雾化器,空气压缩式雾化器,网式雾化器。超声雾化器因其价格合适,适合家用,被广泛应用于加湿器,香薰机等各种小家电,也在医疗领域有很多的应用。家用雾化器的主要器件是一枚雾化片。 家用雾化器分为两种不同类别雾化片,实孔雾化片和微孔雾化片,这两种的喷雾方法和工作原理是有所不同的。不同的雾化片需要特定的频率才能工作。 实孔雾

qq_42415953的博客 2万+

Linux内核支持串口屏,linux串口触摸屏设计总结

Linux serial touch 设计总结概述:最近在做嵌入式linux下串口触摸屏设计,遇到一些问题,经过查找资料和请教同事,总算把问题解决了,事后有把linux相关的内核代码仔细看了一遍,为了有点成果,特别写了个总结。如有任何问题请联系yxj_5421@http://www.doczj.com/doc/87b94b1e227916888486d76d.html,转载请标明出处。系统资源:L...

weixin_39935903的博客 1296

全面了解ThreadPoolExecutor线程池各项参数

全面了解ThreadPoolExecutor线程池

做一个有思想的技术人 5659

独立游戏开发者必看:STEAM税务信息更新全流程(附中国区避坑指南)

本文为独立游戏开发者详细解析了Steam税务信息更新的全流程,重点针对中国区开发者提供了避坑指南。文章深入解释了W-8BEN表格的填写要点,并指导如何准备个人所得税纳税记录和身份证等核心证明文件,以确保顺利通过审核,避免收入被预扣税款。

tcp8optimizer的博客 1273

Java线程池七个参数详解

java多线程开发时,常常用到线程池技术,这篇文章是对创建java线程池时的七个参数的详细解释。 从源码中可以看出,线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadF...

IT小跟班 30万+

LangGraph 实战教程:搭建一个智能办公多Agent协作平台

只存储必要的信息:不要把所有的原始数据都存储在状态中,比如销售数据可以存储在数据库或向量数据库中,状态中只存储数据的 ID 或摘要。使用类型提示:避免类型错误,提高代码的可维护性。支持增量更新:LangGraph 默认支持状态的增量更新(通过reduce函数),比如messages列表可以通过追加新消息的方式更新,而不是每次都重新创建整个列表。Normal Edge 的定义非常简单,只需要在StateGraph对象(稍后会详细讲解)上调用add_edge# 创建一个 StateGraph 对象。

SuperAGI2025 221

【网络与爬虫 62】小程序爬虫终极攻略:微信支付宝小程序数据采集完全指南

本文深入解析微信和支付宝小程序的爬虫技术,从基础架构分析到高级逆向工程,涵盖WXML/AXML模板解析、API接口拦截、网络流量分析等核心技术。通过实战案例和代码示例,帮助开发者掌握小程序数据采集的完整技术栈。

熵数实验室 3415

线程池ThreadPoolExecutor参数介绍、参数选型、任务执行流程

线程池ThreadPoolExecutor总结1、参数介绍2、任务提交执行流程3、参数设置 1、参数介绍 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,

jingjiu1的博客 987

ThreadPoolExecutor 线程池

在开发服务器端软件项目时,软件经常需要处理执行时间很短而数目巨大的请求,如果为每一个请求创建一个新的线程,则会导致性能上的瓶颈。因为JVM 需要频繁地处理线程对象的创建和销毁,如果请求的执行时间很短,则有可能花在创建和销毁线程对象上的时间大于真正执行任务的时间,所以系统性能会大幅降低。JDK 5及以上版本提供了对线程池的支持,主要用于支持高并发的访问处理,并且复用线程对象。

m0_62943934的博客 3549

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

为什么阿里的Java开发规范中禁止使用Executors创建线程池?

最近壹哥有个学生出去面试,面试官的一个问题是:在开发中你使用什么方式创建线程池?这个学生答曰:使用jdk中自带的工厂类Executors 创建线程池!该学生回答完问题后,感觉面试官对此答案不是很满意,于是就跑回来问壹哥。那么接下来,壹哥就和大家来分享一下这道面试题的标准答案。 其实这个问题的答案,在阿里巴巴开发规范1.4版中,早就有明确的答案,我们先来看看阿里巴巴开发规范中对于这一段描述的截图:从上图中我们不难看出,创建线程池,最好直接使用 ThreadPoolExecutor 类的构造方法去创建!那么为什

一一哥 1482

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

Java线程池ThreadPoolExecutor使用与解析

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33672400的博客 197

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java线程池ThreadPool详解

Java线程池ThreadPool详解1. 线程池概述1.1 线程池简介1.2 线程池特点1.3 线程池解决的问题2. 线程池原理分析2.1 线程池总体设计2.6 线程池流转状态2.2 线程池执行流程2.3 线程池核心参数2.4 线程池饱和策略2.5 线程池阻塞队列2.7 关闭线程池3. 线程池工具类4. Executors创建线程池注意事项5. 线程池手动创建6. SpringBoot创建线程池 1. 线程池概述 1.1 线程池简介 线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出

靖节先生的博客 8488

Executor以及Executors源码分析

基于JDK1.8源码进行分析的。 首先我们看一下Executor接口的源码。 public interface Executor { void execute(Runnable command); } 该接口定义了一个规范,也就是execute方法的规范,里面需要传入Runnable类或者其子类的对象。该接口有一个实现接口,ExecutorService。下面我们看一下该接口的源码...

Burgess_Lee的博客 548

大厂面试丨为什么阿里的Java开发规范,禁止使用Executors创建线程池?!

一. 问题概述最近耀哥有个学生出去面试,面试官的一个问题是:在开发中你使用什么方式创建线程池?这个学生答曰:使用jdk中自带的工厂类Executors 创建线程池!该学生回答完问题后,感觉面试官对此答案不是很满意,于是就跑回来问耀哥。那么接下来,耀哥就和大家来分享一下这道面试题的标准答案,看看怎么回答才能拿高分!二. 问题的答案其实这个问题的答案,在阿里巴巴开发规范1.4版中,早就有明确的答案,我们先来看看阿里巴巴开发规范中对于这一段描述的截图:从上图中我们不难看出,创建线程池,最好直接使用 ThreadP

威哥爱编程,华为 HDE,鸿蒙极客、优快云博客专家、《鸿蒙 HarmonyOS 5/6 开发之路》系列图书第一作者 924

线程池参数解析

线程池ThreadPoolExecutor、Executors参数详解与源代码分析 出处:http://www.cnblogs.com/nullzx/ 1. ThreadPoolExecutor数据成员 1 Private finalAtomicInteger ctl = newAtomicInteger(ctlOf(RUNNING,0)); ...

Mrxingyong的博客 308

Executors的四种线程池

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永

看风人Z的博客 3339

java多线程与线程池-02线程池与锁

ThreadPoolExecutor是应用最广的底层线程池类,它实现了Executor和ExecutorService接口。

天空鸟的博客 679

ThreadPoolExecutor 的参数含义及源码执行流程

线程池是为了避免线程频繁的创建和销毁带来的性能消耗,而建立的一种池化技术,它是把已创建的线程放入“池”中,当有任务来临时就可以重用已有的线程,无需等待创建的过程,这样就可以有效提高程序的响应速度。但如果要说线程池的话一定离不开 ThreadPoolExecutor ,在阿里巴巴的《Java 开发手册》中是这样规定线程池的:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的读者更加明确线程池的运行规则,规避资源耗尽的风险。说明:Execu

BASK2311的博客 518
上一篇: RabbitMQ高可用系列之消费失败处理-死信
下一篇: RPC原理
牧竹子
博客等级 码龄17年 572粉丝 233原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值