ThreadLocal使用的N个demo

405 阅读1分钟

当你学习使用一个陌生的关键字的时候,一定是记住他的使用情况,然后再去分析源码,最后再完完全全的掌握它,本文详细介绍一下ThreadLocal的使用案例

  1. 复制成员变量为线程本地变量
public class ThreadLocalTest {

    //定义线程本地变量
    private static final ThreadLocal<Foo> LOCAL_FOO = ThreadLocal.withInitial(() -> new Foo());

    private static final ThreadPoolExecutor POOL_EXECUTOR =
            new ThreadPoolExecutor(
                    10,
                    10,
                    20,
                    TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(2000));

    @Test
    public void testThreadLocal() throws InterruptedException {
        //共5个线程
        for (int i = 0; i < 5; i++) {
            POOL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    //获取“线程本地变量”中当前线程所绑定的值
                    if (LOCAL_FOO.get() == null) {
                        //设置“线程本地变量”中当前线程所绑定的值
                        System.out.println(Thread.currentThread().getName() + ":执行了set");
                        LOCAL_FOO.set(new Foo());
                    }

                    System.out.println(Thread.currentThread().getName() + ":初始的本地值:" + LOCAL_FOO.get());

                    //每个线程执行10次
                    for (int i = 0; i < 10; i++) {
                        Foo foo = LOCAL_FOO.get();
                        foo.setBar(foo.getBar() + 1);
                        LockSupport.parkNanos(10 * 1000L * 1000L);

                    }
                    System.out.println(Thread.currentThread().getName() + ":累加10次之后的本地值:" + LOCAL_FOO.get());
                    //删除“线程本地变量”中当前线程所绑定的值,对于线程池中的线程尤其重要
                    LOCAL_FOO.remove();
                }
            });
        }

        ThreadUtil.sleepMilliSeconds(Integer.MAX_VALUE);
    }

    @Data
    static class Foo {
        //实例总数
        static final AtomicInteger AMOUNT = new AtomicInteger(0);
        int index = 0;  //对象的编号
        int bar = 10; //对象的内容

        //构造器
        public Foo() {
            index = AMOUNT.incrementAndGet(); //总数增加,并且给对象的编号
            System.out.println(Thread.currentThread().getName() + "execute constructor index:"+ index );
        }

        @Override
        public String toString() {
            return index + "@Foo{bar=" + bar + '}';
        }
    }
}
总结:
    static final修饰的成员变量并不会初始化,只有在调用get的时候才会init
    
  1. ThreadLocal修饰保存本地变量,在各个服务之间传递参数
public class ThreadLocalTest2 {
  /** 模拟业务方法 */
  public void serviceMethod() {
    // 睡眠500ms,模拟执行耗时
    LockSupport.parkNanos(500 * 1000L * 1000L);

    // 记录开始调用到这个点p1的耗时
    TimeLog.logPoint("point-1 service");

    // 调用下一层方法:模拟dao业务方法
    daoMethod();

    // 调用下一层方法:模拟RPC远程业务方法
    rpcMethod();
  }

  /** 模拟dao业务方法 */
  public void daoMethod() {
    // 睡眠400ms,模拟执行耗时
    LockSupport.parkNanos(400 * 1000L * 1000L);

    // 记录上一个点p1到这里p2的耗时
    TimeLog.logPoint("point-2 dao");
  }

  /** 模拟RPC远程业务方法 */
  public void rpcMethod() {
    // 睡眠400ms,模拟执行耗时
    LockSupport.parkNanos(600 * 1000L * 1000L);

    // 记录上一个点p2到这里p3的耗时
    TimeLog.logPoint("point-3 rpc");
  }

  /** 测试用例:线程方法调用的耗时 */
  @org.junit.Test
  public void testTimeLog() throws InterruptedException {
    Runnable runnable =
        () -> {
          // 开始耗时记录
          TimeLog.beginSpeedLog();

          // 调用模拟业务方法
          serviceMethod();

          // 打印耗时
          TimeLog.printCost();

          // 结束耗时记录
          TimeLog.endSpeedLog();
        };
    new Thread(runnable).start();
    LockSupport.parkNanos(10 * 1000L * 1000L * 1000L); // 等待10s看结果
  }
}


public class TimeLog {
  /** 记录调用耗时的本地Map变量 */
  private static final ThreadLocal<Map<String, Long>> TIME_RECORD_LOCAL =
      ThreadLocal.withInitial(TimeLog::initialStartTime);

  /** 记录调用耗时的本地Map变量的初始化方法 */
  public static Map<String, Long> initialStartTime() {
    Map<String, Long> map = new HashMap<>();
    map.put("start", System.currentTimeMillis());
    map.put("last", System.currentTimeMillis());
    return map;
  }

  /** 开始耗时记录 */
  public static final void beginSpeedLog() {
    System.out.println("开始耗时记录");
    TIME_RECORD_LOCAL.get();
  }

  /** 结束耗时记录 */
  public static final void endSpeedLog() {
    TIME_RECORD_LOCAL.remove();
    System.out.println("结束耗时记录");
  }

  /** 记录方法的耗时 */
  public static final void logPoint(String point) {
    // 获取上一次的时间
    Long last = TIME_RECORD_LOCAL.get().get("last");
    // 计算耗时,并且保存
    Long cost = System.currentTimeMillis() - last;
    TIME_RECORD_LOCAL.get().put(point + " cost:", cost);
    // 保存最近时间,供下一次使用
    TIME_RECORD_LOCAL.get().put("last", System.currentTimeMillis());
  }

  /** print方法的耗时 */
  public static final void printCost() {

    Iterator<Map.Entry<String, Long>> it = TIME_RECORD_LOCAL.get().entrySet().iterator();
    while (it.hasNext()) {
      Map.Entry<String, Long> entry = it.next();
      System.out.println(Thread.currentThread().getName() + ":" + entry.getKey() + " =>" + entry.getValue());
    }
  }
}