Java线程池趣味事:这不是线程池

线程池,不说本身很简单,但应用一定是简单的。

线程池有许多的实现,但我们只说 ThreadPoolExecutor 版本,因其应用最广泛,别无其他。当然了,还有一个定时调度线程池 ScheduledThreadPoolExecutor 另说,因其需求场景不同,无法比较。

下面,我就几个应用级别,说明下我们如何快速使用线程池。(走走过场而已,无关其他)

1.1. 初级线程池

==============

初级版本的使用线程池,只需要借助一个工具类即可: Executors . 它提供了许多静态方法,你只需随便选一个就可以使用线程池了。比如:

// 创建固定数量的线程池

Executors.newFixedThreadPool(8);

// 创建无限动态创建的线程池

Executors.newCachedThreadPool();

// 创建定时调度线程池

Executors.newScheduledThreadPool(2);

// 还有个创建单线程的就不说了,都一样

使用上面这些方法创建好的线程池,直接调用其 execute() 或者 submit() 方法,就可以实现多线程编程了。没毛病!

1.2. 中级线程池

==============

我这里所说的中级,实际就是不使用以上超级简单方式使用线程池的方式。即你已经知道了 ThreadPoolExecutor 这个东东了。这不管你的出发点是啥!

// 自定义各线程参数

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 20, 20, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

具体参数解释就不说了,咱们不扫盲。总之,使用这玩意儿,说明你已经开始有点门道了。

1.3. 高级线程池

==============

实际上,这个版本就没法具体说如何做了。

但它可能是,你知道你的线程池应用场景的,你清楚你的硬件运行环境的,你会使用线程池命名的,你会定义你的队列大小的,你会考虑上下文切换的,你会考虑线程安全的,你会考虑锁性能的,你可能会自己造个轮子的。。。

2. 这不是线程池

==============

我们通常理解的线程池,就是能够同时跑多个任务的地方。但有时候线程池不一像线程池,而像一个单线程。来看一个具体的简单的线程池的使用场景:

// 初始化线程池

private ExecutorService executor

= new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),

Runtime.getRuntime().availableProcessors(),

0L, TimeUnit.SECONDS,

new ArrayBlockingQueue<>(50),

new NamedThreadFactory(“test-pool”),

new ThreadPoolExecutor.CallerRunsPolicy());

// 使用线程池处理任务

public Integer doTask(String updateIntervalDesc) throws Exception {

long startTime = System.currentTimeMillis();

List testList;

AtomicInteger affectNum = new AtomicInteger(0);

int pageSize = 1000;

AtomicInteger pageNo = new AtomicInteger(1);

Map<String, Object> condGroupLabel = new HashMap<>();

log.info(“start do sth:{}”, updateIntervalDesc);

List<Future<?>> futureList = new ArrayList<>();

do {

PageHelper.startPage(pageNo.getAndIncrement(), pageSize);

List list

= testDao.getLabelListNew(condGroupLabel);

testList = list;

// 循环向线程池中提交任务

for (TestDto s : list) {

Future<?> future = executor.submit(() -> {

try {

// do sth…

affectNum.incrementAndGet();

}

catch (Throwable e) {

log.error(“error:{}”, pageNo.get(), e);

}

});

futureList.add(future);

}

} while (testList.size() >= pageSize);

// 等待任务完成

int i = 0;

for (Future<?> future : futureList) {

future.get();

log.info("done:+{} ", i++);

}

log.info(“doTask done:{}, num:{}, cost:{}ms”,

updateIntervalDesc, affectNum.get(), System.currentTimeMillis() - startTime);

return affectNum.get();

}

主要业务就是,从数据库中取出许多任务,放入线程池中运行。因为任务又涉及到db等的io操作,所以使用多线程处理,非常合理。

然而,有一种情况的出现,也许会打破这个平衡:那就是当单个任务能够快速执行完成时,而且快到刚上一任务提交完成,还没等下一次提交时,就任务就已被执行完成。这时,你就可能会看到一个神奇的现象,即一直只有一个线程在运行任务。这不是线程池该干的事,更像是单线程任务在跑。

然后,我们可能开始怀疑:某个线程被阻塞了?线程调度不公平了?队列选择不正确了?触发jdk bug了?线程池未完全利用的线程了?等等。。。

然而结果并非如此,究其原因只是当我们向线程池提交任务时,实际上只是向线程池的队列中添加了任务。即上面显示的 ArrayBlockingQueue 添加了任务,而线程池中的各worker负责从队列中获取任务进行执行。而当任务数很少时,自然只有一部分worker会处理执行中了。至于为什么一直是同一个线程在执行,则可能是由于jvm的调度机制导致。事实上,是受制于 ArrayBlockingQueue.poll() 的公平性。而这个poll()的实现原理,则是由 wait/notify 机制的公平性决定的。

如下,是线程池的worker工作原理:

// java.util.concurrent.ThreadPoolExecutor#runWorker

/**

  • Main worker run loop. Repeatedly gets tasks from queue and

  • executes them, while coping with a number of issues:

    1. We may start out with an initial task, in which case we
  • don’t need to get the first one. Otherwise, as long as pool is

  • running, we get tasks from getTask. If it returns null then the

  • worker exits due to changed pool state or configuration

  • parameters. Other exits result from exception throws in

  • external code, in which case completedAbruptly holds, which

  • usually leads processWorkerExit to replace this thread.

    1. Before running any task, the lock is acquired to prevent
  • other pool interrupts while the task is executing, and then we

  • ensure that unless pool is stopping, this thread does not have

  • its interrupt set.

    1. Each task run is preceded by a call to beforeExecute, which
  • might throw an exception, in which case we cause thread to die

  • (breaking loop with completedAbruptly true) without processing

  • the task.

    1. Assuming beforeExecute completes normally, we run the task,
  • gathering any of its thrown exceptions to send to afterExecute.

  • 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.

    1. After task.run completes, we call afterExecute, which may
  • also throw an exception, which will also cause thread to

  • die. According to JLS Sec 14.20, this exception is the one that

  • will be in effect even if task.run throws.

  • The net effect of the exception mechanics is that afterExecute

  • and the thread’s UncaughtExceptionHandler have as accurate

  • information as we can provide about any problems encountered by

  • user code.

  • @param w the worker

*/

final void runWorker(Worker w) {

Thread wt = Thread.currentThread();

Runnable task = w.firstTask;

w.firstTask = null;

w.unlock(); // allow interrupts

boolean completedAbruptly = true;

try {

// worker 不停地向队列中获取任务,然后执行

// 其中获取任务的过程,可能被中断,也可能不会,受到线程池伸缩配置的影响

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

}

}

/**

  • 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).

    1. The pool is stopped.
    1. The pool is shutdown and the queue is empty.
    1. 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, and if the queue is

  • non-empty, this worker is not the last thread in the pool.

  • @return task, or null if the worker must exit, in which case

  •     workerCount is decremented
    

*/

private Runnable getTask() {

boolean timedOut = false; // Did the last poll() time out?

for (;😉 {

int c = ctl.get();

int rs = runStateOf©;

// Check if queue empty only if necessary.

if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {

decrementWorkerCount();

return null;

}

int wc = workerCountOf©;

《MySql面试专题》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySql性能优化的21个最佳实践》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

《MySQL高级知识笔记》

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

全网火爆MySql 开源笔记,图文并茂易上手,阿里P8都说好

关注我,点赞本文给更多有需要的人

片转存中…(img-TmccBpsP-1714386804699)]

[外链图片转存中…(img-e47anH66-1714386804700)]

[外链图片转存中…(img-WEhOcSB8-1714386804701)]

[外链图片转存中…(img-mXf6k8Vd-1714386804701)]

[外链图片转存中…(img-mPqbd8gD-1714386804702)]

[外链图片转存中…(img-2mCVSjs1-1714386804702)]

[外链图片转存中…(img-Nnks3MHr-1714386804703)]

[外链图片转存中…(img-q7UI0CcN-1714386804703)]

文中展示的资料包括:**《MySql思维导图》《MySql核心笔记》《MySql调优笔记》《MySql面试专题》《MySql性能优化的21个最佳实践》《MySq高级知识笔记》**如下图

[外链图片转存中…(img-bQ41nvV8-1714386804703)]

关注我,点赞本文给更多有需要的人

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值