Java concurrent API提供了许多有用的库和功能,这些功能使使用异步和多线程控制流变得更加容易。其中的一项功能是Thread Pools。
今天早些时候,我正在尝试并发API,并注意到一些有关从线程池中选择线程的奇怪模式。他们让我想知道线程选择背后的逻辑是什么。下面是一些示例代码,也许可以为您提供我正在谈论的示例。当程序记录每个[
时,您会注意到线程名中的模式(在]
tick
内部)。但是,该模式可能不会在64位Windows 10计算机上的Oracle JDK 1.8.0_161之外的JDK构建中出现。
无论如何,我的问题与任何巧合模式无关,而是关于从线程池中选择线程的过程。该模式让我相信它不是完全随机的,那么此选择背后的逻辑是什么?谢谢。 :)
public static void main(String[] args)
{
// create a ScheduledExecutorService with a Thread Pool of 7 threads
ScheduledExecutorService ses = Executors.newScheduledThreadPool(7);
log("go");
// starts a timer of 30 seconds, shutting down ses afterwards
ses.schedule(() -> call(ses), 30, TimeUnit.SECONDS);
// starts the ticker
ses.schedule(() -> tick(ses, 1), 1, TimeUnit.SECONDS);
}
// ticks once per second, logging the current tick counter. (i.e, counts
// by 1 each second) ticking ends when ses is shutdown.
public static void tick(ScheduledExecutorService ses, int count)
{
if (!ses.isShutdown())
{
log("tick %d", count);
ses.schedule(() -> tick(ses, count + 1), 1, TimeUnit.SECONDS);
}
}
// called when it's time to shutdown ses.
public static void call(ScheduledExecutorService ses)
{
log("done");
ses.shutdown();
}
// formats and logs the given message alongside a timestamp and the name
// of the executing thread
public static void log(String s, Object...args)
{
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String time = sdf.format(new Date(System.currentTimeMillis()));
String thread = Thread.currentThread().getName();
String message = String.format(s, args);
String log = String.format("%s [%s] - %s", time, thread, message);
System.out.println(log);
}
答案 0 :(得分:0)
您创建的ScheduledThreadPool
内部有一个DelayQueue
。提交任务时,它将始终创建一个新的工作线程,该线程将继续从DelayQueue
接收任务。
提交任务时的代码,新线程继续执行任务
//getTask() method calls DelayQueue#take()
while (task != null || (task = getTask()) != null) {
......
task.run();
......
}
如果take()
方法返回零,则getDelay
方法将返回一个任务,否则工作线程将变为等待倒计时结束。当时间到时,它将唤醒工作线程以获取任务。 。
DelayQueue#take()的代码
/**
* Retrieves and removes the head of this queue, waiting if necessary
* until an element with an expired delay is available on this queue.
*
* @return the head of this queue
* @throws InterruptedException {@inheritDoc}
*/
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
for (;;) {
E first = q.peek();
if (first == null)
available.await();
else {
long delay = first.getDelay(NANOSECONDS);
if (delay <= 0)
return q.poll();
first = null; // don't retain ref while waiting
if (leader != null)
available.await();
else {
Thread thisThread = Thread.currentThread();
leader = thisThread;
try {
available.awaitNanos(delay);
} finally {
if (leader == thisThread)
leader = null;
}
}
}
}
} finally {
if (leader == null && q.peek() != null)
available.signal();
lock.unlock();
}
}
当您提交7个任务时,有7个工作线程正在等待被唤醒。 因此线程选择取决于唤醒策略。通常,它是根据等待队列的顺序来唤醒的。