结论
场景:想尽量减少队列等待(设置线程队列长度为1),直接用线程池处理,并发数量设置的够用(示例设置4个线程),期望在4个请求内,应该都不进入等待队列。
结论证明是错误的:在空余线程够用(小于4)的情况下,依然会先进入队列,并且超出队列长度1后,会发生拒绝。
测试代码
JAVA
package fly.sample.thread;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor 的内部实现
*/
public class SpringThreadPoolTest {
public static void main(String[] args) throws Exception {
ApplicationContext factory = new ClassPathXmlApplicationContext("classpath:spring-pool.xml");
ThreadPoolTaskExecutor executor = factory.getBean("threadPoolTaskExecutor", ThreadPoolTaskExecutor.class);
List<Future> futures = new ArrayList<>();
long stime = System.currentTimeMillis();
System.out.println("第一批");
for (int i = 1; i <= 10; i++) {
try {
Future<Integer> future = executor.submit(new MyCallable(i));
futures.add(future);
System.out.println("时间:" + (System.currentTimeMillis() - stime) + " " + "提交成功:" + i + " " + executor.getThreadPoolExecutor());
} catch (Exception e) {
System.out.println("时间:" + (System.currentTimeMillis() - stime) + " " + "提交失败:" + i + " " + e.getMessage());
// e.printStackTrace();
}
}
Thread.sleep(205);
System.out.println("第二批");
for (int i = 11; i <= 20; i++) {
try {
Future<Integer> future = executor.submit(new MyCallable(i));
futures.add(future);
System.out.println("时间:" + (System.currentTimeMillis() - stime) + " " + "提交成功:" + i + " " + executor.getThreadPoolExecutor());
} catch (Exception e) {
System.out.println("时间:" + (System.currentTimeMillis() - stime) + " " + "提交失败:" + i + " " + e.getMessage());
// e.printStackTrace();
}
}
System.out.println(futures.size());
for (Future future : futures) {
System.out.println("时间:" + (System.currentTimeMillis() - stime) + " " + "get:" + future.get(105, TimeUnit.MILLISECONDS));
}
}
static class MyCallable implements Callable {
int result = 0;
public MyCallable(int result) {
this.result = result;
}
@Override
public Object call() throws Exception {
Thread.sleep(100);
System.out.println("完成:" + result);
return result;
}
}
}
spring-pool.xml
<bean id="threadPoolTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" lazy-init="true" scope="singleton">
<property name="corePoolSize" value="4"/>
<property name="maxPoolSize" value="4"/>
<property name="queueCapacity" value="1"/>
<property name="keepAliveSeconds" value="300"/>
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$AbortPolicy"/>
</property>
</bean>
结果分析
结果如下:从现象来看:4个并发+1个队列长度,分两批提交,第二批等待第一批任务全部完成:从现象来看:
- 第一次提交成功5个,符合预期。
- 第二次提交成功2个后开始拒绝,不符合预期(13被拒绝了),空余线程是够的,应该是先进队列,如果队列来不及放入执行,就会出现拒绝的情况。
第一批
时间:4 提交成功:1 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
时间:22 提交成功:2 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
时间:23 提交成功:3 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
时间:23 提交成功:4 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]
时间:23 提交成功:5 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]
时间:23 提交失败:6 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@470f1802
时间:24 提交失败:7 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@3e92efc3
时间:24 提交失败:8 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@72a7c7e0
时间:24 提交失败:9 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@70e8f8e
时间:24 提交失败:10 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@5bd03f44
完成:1
完成:2
完成:3
完成:4
完成:5
第二批
时间:234 提交成功:11 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 1, queued tasks = 0, completed tasks = 5]
时间:235 提交成功:12 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 1, queued tasks = 1, completed tasks = 5]
时间:235 提交失败:13 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 2, queued tasks = 0, completed tasks = 5]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@5a63f509
时间:236 提交成功:14 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 2, queued tasks = 1, completed tasks = 5]
时间:236 提交失败:15 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 2, queued tasks = 1, completed tasks = 5]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@34b7ac2f
时间:236 提交失败:16 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 2, queued tasks = 1, completed tasks = 5]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@4b0b0854
时间:236 提交失败:17 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 2, queued tasks = 1, completed tasks = 5]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@10e41621
时间:236 提交失败:18 Executor [java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 2, queued tasks = 0, completed tasks = 5]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@2667f029
时间:236 提交成功:19 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 3, queued tasks = 1, completed tasks = 5]
时间:236 提交成功:20 java.util.concurrent.ThreadPoolExecutor@418e7838[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 5]
设置队列长度为0
队列设置0,使用的SynchronousQueue,执行符合预期,结果如下 SynchronousQueue没有容量,是无缓冲等待队列,是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素。
第一批
时间:2 提交成功:1 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
时间:17 提交成功:2 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 2, active threads = 2, queued tasks = 0, completed tasks = 0]
时间:17 提交成功:3 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 3, active threads = 3, queued tasks = 0, completed tasks = 0]
时间:17 提交成功:4 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]
时间:17 提交失败:5 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@63021689
时间:18 提交失败:6 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@1622f1b
时间:18 提交失败:7 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@2e4b8173
时间:18 提交失败:8 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@17046283
时间:18 提交失败:9 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@29626d54
时间:18 提交失败:10 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 0]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@6e4784bc
完成:1
完成:2
完成:4
完成:3
第二批
时间:228 提交成功:11 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 1, queued tasks = 0, completed tasks = 4]
时间:229 提交成功:12 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 1, queued tasks = 0, completed tasks = 4]
时间:229 提交成功:13 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 2, queued tasks = 0, completed tasks = 4]
时间:229 提交成功:14 java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 3, queued tasks = 0, completed tasks = 4]
时间:229 提交失败:15 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 4]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@e056f20
时间:230 提交失败:16 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 4]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@19bb07ed
时间:230 提交失败:17 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 4]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@353d0772
时间:230 提交失败:18 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 4]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@67a20f67
时间:230 提交失败:19 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 4]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@a9cd3b1
时间:230 提交失败:20 Executor [java.util.concurrent.ThreadPoolExecutor@61230f6a[Running, pool size = 4, active threads = 4, queued tasks = 0, completed tasks = 4]] did not accept task: fly.sample.thread.threadpool.SpringThreadPoolTest$MyCallable@64cd705f
加入线程池源码
只有第一批不进入队列,后面的就算线程空闲,也会先进入队列。 java.util.concurrent.ThreadPoolExecutor- public void execute(Runnable command)
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);