线程池中使用线程变量ThreadLocal 的注意以及线程池创建

17 篇文章 1 订阅
6 篇文章 0 订阅

从输出可以看出,线程池中的线程执行了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<String> CTX = new ThreadLocal<String>();

    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:线程池中任务有三种排队策略:

  1. 直接提交。直接提交策略表示线程池不对任务进行缓存。新进任务直接提交给线程池,当线程池中没有空闲线程时,创建一个新的线程处理此任务。这种策略需要线程池具有无限增长的可能性。实现为:SynchronousQueue
  2. 有界队列。当线程池中线程达到corePoolSize时,新进任务被放在队列里排队等待处理。有界队列(如ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
  3. 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
     

                 流程图展示效果如图所示

线程销毁:

       如果线程池中的线程数量大于 corePoolSize 时,如果某线程空闲时间超过 keepAliveTime,线程将被终止,直至线程池中的线程数目不大于 corePooIsize;

        如果允许为核心线程池中的线程设置存活时间,那么核心线程池中的线程空闲时问超过 keepAliveTime,线程也会被终止。

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值