从输出可以看出,线程池中的线程执行了10次,由于创建了固定的线程池就只有3个,与预期的只有一个线程才能拿到线程变量有很大差距;造成这种现象是因为线程复用导致的;
测试代码:
public class Demo {
public static void main(String[] args) throws Exception {
//创建可缓存线程池 无限大小
//ExecutorService executorService = Executors.newCachedThreadPool();
//可固定线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);//测试代码,实际操作不建议用此方法创建线程池
for (int i = 0; i < 10; i++) {
final int temp = i;
executorService.execute(new Runnable() {
@Override
public void run() {
try {
if (temp == 0) {
Ctx.setCtx("您好, yulang!" + temp);
}
Class> clazz = Class.forName("yulang.tools.ctx.ReflectSv");
Method method = clazz.getMethod("getCtx");
Object invoke = method.invoke(clazz.newInstance());
//线程池由于未创建新的线程,导致线程变量也是之前的内容,有了结论,修改就很简单了,一种直接在进入线程时设置为null,另一种是在使用后清空,
System.out.println(Thread.currentThread().getName() + ",拿到的线程变量为:" + invoke);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
}
public class Ctx {
//static防止GC回收
private static final ThreadLocal CTX = new ThreadLocal();
static String getCtx(){
return CTX.get();
}
static void setCtx(String object){
CTX.set(object);
}
static void remove(){
CTX.remove();
}
}
线程池由于未创建新的线程,导致线程变量也是之前的内容。
两种操作:
一、直接在进入线程时remove操作。
二、使用后清空。
实际中应使用:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
参数名、说明:
* corePoolSize 线程池维护线程的最少数量
* maximumPoolSize 线程池维护线程的最大数量
* keepAliveTime 线程池维护线程所允许的空闲时间
* workQueue 任务队列,用来存放我们所定义的任务处理线程
* threadFactory 线程创建工厂
* handler 线程池对拒绝任务的处理策略
其中拒绝策略包含:
1、CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); }}
这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。(开始我总不想丢弃任务的执行,但是对某些应用场景来讲,很有可能造成当前线程也被阻塞。如果所有线程都是不能执行的,很可能导致程序没法继续跑了。需要视业务情景而定吧。)
2、AbortPolicy:处理程序遭到拒绝将抛出运行时 RejectedExecutionException
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException();}
这种策略直接抛出异常,丢弃任务。(jdk默认策略,队列满并线程满时直接拒绝添加新任务,并抛出异常,所以说有时候放弃也是一种勇气,为了保证后续任务的正常进行,丢弃一些也是可以接收的,记得做好记录)
3、DiscardPolicy:不能执行的任务将被删除
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}
这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。
4、DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) {e.getQueue().poll();e.execute(r); }}
该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。这个策略需要适当小心。
workQueue:线程池中任务有三种排队策略:
流程图展示效果如图所示
如果线程池中的线程数量大于 corePoolSize 时,如果某线程空闲时间超过 keepAliveTime,线程将被终止,直至线程池中的线程数目不大于 corePooIsize;
如果允许为核心线程池中的线程设置存活时间,那么核心线程池中的线程空闲时问超过 keepAliveTime,线程也会被终止。