前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThreadLocal探索使用

ThreadLocal探索使用

作者头像
九转成圣
发布2024-04-10 18:22:43
660
发布2024-04-10 18:22:43
举报
文章被收录于专栏:csdncsdn

ThreadLocal探索使用

标签:多线程

场景:利用SimpleDateFormat格式化时间,因为SimpleDateFormat线程不安全,每次都new一个优点浪费,想着每个线程内部维持1个SimpleDateFormat,然而自定义Pet是可以做到的,SimpleDateFormat做不到,好奇怪啊

摘要:Thread有个成员变量 ThreadLocalMap ,key为ThreadLocal,只要拿到key就能操作线程的 ThreadLocalMap,要保证是同一个ThreadLocal对象,否则获取不到,怎么保证是同一个,那就只能把ThreadLocal存起来了,另外注意ThreadLocal用完之后记得remove

耗时:1天

有坑版本

代码语言:javascript
复制
private static void extracted4() throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(10);
    // 第一个坑:多个线程操作一个map,应该用ConcurrentHashMap
    Map<String, ThreadLocal<Pet>> threadLocalMaps = new HashMap<>();
    Map<String, List<Pet>> statistics = new HashMap<>();
    CountDownLatch countDownLatch = new CountDownLatch(1000);
    for (int i = 0; i < 1000; i++) {
        threadPool.submit(() -> {
            String threadName = Thread.currentThread().getName();
            ThreadLocal<Pet> threadLocal = threadLocalMaps.get(threadName);
            if (threadLocal == null) {
                /**
                     * 新建一个tl 保证每个线程里面都有一个自己的tl,
                     * Thread有个成员变量 ThreadLocalMap ,key为ThreadLocal,只要拿到key就能操作线程的 ThreadLocalMap
                     */
                // 第二个坑:并发问题
                threadLocal = ThreadLocal.withInitial(Pet::new);
                threadLocalMaps.put(threadName, threadLocal);
            }
            Pet sdf = threadLocal.get();
            List<Pet> pets = statistics.computeIfAbsent(threadName, k -> new ArrayList<>());
            pets.add(sdf);
            countDownLatch.countDown();
        });
    }
    // 坑3:main线程没有等待所有任务跑完
    countDownLatch.await();
    // 防止内存泄露,项目里面的线程池运行期间一般不会shutdown的
    for (ThreadLocal<Pet> threadLocal4remove : threadLocalMaps.values()) {
        threadLocal4remove.remove();
    }
    threadPool.shutdown();
    // 结果统计 一个线程里面只有一个Pet
    int count = 0;
    for (Map.Entry<String, List<Pet>> entry : statistics.entrySet()) {
        String key = entry.getKey();
        List<Pet> pets = entry.getValue();
        int size = pets.size();
        count += size;
        System.out.println(key + "\t" + size + "\t" + new HashSet<>(pets).size());
    }
    System.out.println(count);
}
代码语言:javascript
复制
pool-1-thread-1	102	2(预期是1,坑2导致的)
pool-1-thread-3	81	1
pool-1-thread-2	94	1
pool-1-thread-5	119	1
pool-1-thread-4	72	2
pool-1-thread-7	81	1
pool-1-thread-6	80	2
pool-1-thread-9	181	1
pool-1-thread-10	100	1
pool-1-thread-8	89	1
999(坑1导致的)

无坑版本

暂且就当无坑版本吧

代码语言:javascript
复制
private static void extracted4() throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(10);
    Map<String, ThreadLocal<Pet>> threadLocalMaps = new HashMap<>();
    // 缺陷1.多个线程操作一个map,应该用ConcurrentHashMap
    Map<String, List<Pet>> statistics = new ConcurrentHashMap<>();
    CountDownLatch countDownLatch = new CountDownLatch(1000);
    Object lock = new Object();
    for (int i = 0; i < 1000; i++) {
        threadPool.submit(() -> {
            String threadName = Thread.currentThread().getName();
            ThreadLocal<Pet> threadLocal = threadLocalMaps.get(threadName);
            /**
                 * 新建一个tl 保证每个线程里面都有一个自己的tl,
                 * Thread有个成员变量 ThreadLocalMap ,key为ThreadLocal,只要拿到key就能操作线程的 ThreadLocalMap
                 * 注意:双重检查
                 */
            if (threadLocal == null) {
                synchronized (lock) {
                    threadLocal = threadLocalMaps.get(threadName);
                    if (threadLocal == null) {
                        threadLocal = ThreadLocal.withInitial(Pet::new);
                        threadLocalMaps.put(threadName, threadLocal);
                    }
                }
            }
            Pet sdf = threadLocal.get();
            List<Pet> pets = statistics.computeIfAbsent(threadName, k -> new ArrayList<>());
            pets.add(sdf);
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    // 防止内存泄露,项目里面的线程池运行期间一般不会shutdown的
    for (ThreadLocal<Pet> threadLocal4remove : threadLocalMaps.values()) {
        threadLocal4remove.remove();
    }
    threadPool.shutdown();
    // 结果统计 一个线程里面只有一个Pet
    int count = 0;
    HashSet<Pet> sdfSet = new HashSet<>();
    for (Map.Entry<String, List<Pet>> entry : statistics.entrySet()) {
        String key = entry.getKey();
        List<Pet> pets = entry.getValue();
        sdfSet.addAll(pets);
        int size = pets.size();
        count += size;
        System.out.println(key + "\t" + size + "\t" + new HashSet<>(pets).size());
    }
    System.out.println(count);
    System.out.println(sdfSet.size());
}
代码语言:javascript
复制
pool-1-thread-1	95	1
pool-1-thread-3	103	1
pool-1-thread-2	105	1
pool-1-thread-5	87	1
pool-1-thread-4	108	1
pool-1-thread-7	104	1
pool-1-thread-6	107	1
pool-1-thread-10	123	1
pool-1-thread-9	99	1
pool-1-thread-8	69	1
1000
10

通用版本

代码语言:javascript
复制
private static void extractedX(Supplier<Object> supplier) throws InterruptedException {
    ExecutorService threadPool = Executors.newFixedThreadPool(10);
    Map<String, ThreadLocal<Object>> threadLocalMaps = new HashMap<>();
    // 缺陷1.多个线程操作一个map,应该用ConcurrentHashMap
    Map<String, List<Object>> statistics = new ConcurrentHashMap<>();
    CountDownLatch countDownLatch = new CountDownLatch(1000);
    Object lock = new Object();
    for (int i = 0; i < 1000; i++) {
        threadPool.submit(() -> {
            String threadName = Thread.currentThread().getName();
            ThreadLocal<Object> threadLocal = threadLocalMaps.get(threadName);
            /*
             * 新建一个tl 保证每个线程里面都有一个自己的tl,
             * Thread有个成员变量 ThreadLocalMap ,key为ThreadLocal,只要拿到key就能操作线程的 ThreadLocalMap
             * 注意:双重检查
             */
            if (threadLocal == null) {
                synchronized (lock) {
                    threadLocal = threadLocalMaps.get(threadName);
                    if (threadLocal == null) {
                        threadLocal = ThreadLocal.withInitial(supplier);
                        threadLocalMaps.put(threadName, threadLocal);
                    }
                }
            }
            Object sdf = threadLocal.get();
            List<Object> pets = statistics.computeIfAbsent(threadName, k -> new ArrayList<>());
            pets.add(sdf);
            countDownLatch.countDown();
        });
    }
    countDownLatch.await();
    // 防止内存泄露,项目里面的线程池运行期间一般不会shutdown的
    for (ThreadLocal<Object> threadLocal4remove : threadLocalMaps.values()) {
        threadLocal4remove.remove();
    }
    threadPool.shutdown();
    // 结果统计 一个线程里面只有一个Pet
    int count = 0;
    HashSet<Object> sdfSet = new HashSet<>();
    for (Map.Entry<String, List<Object>> entry : statistics.entrySet()) {
        String key = entry.getKey();
        List<Object> pets = entry.getValue();
        sdfSet.addAll(pets);
        int size = pets.size();
        count += size;
        System.out.println(key + "\t" + size + "\t" + new HashSet<>(pets).size());
    }
    System.out.println(count);
    System.out.println(sdfSet.size());
}

why???

SimpleDateFormat哪里特殊吗???

代码语言:javascript
复制
public static void main(String[] args) throws InterruptedException {
    extractedX(Pet::new);
    extractedX(()->new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
代码语言:javascript
复制
pool-1-thread-1	87	1
pool-1-thread-3	124	1
pool-1-thread-2	91	1
pool-1-thread-5	137	1
pool-1-thread-4	94	1
pool-1-thread-7	75	1
pool-1-thread-6	112	1
pool-1-thread-9	89	1
pool-1-thread-10	78	1
pool-1-thread-8	113	1
1000
10
pool-2-thread-9	121	1
pool-2-thread-10	95	1
pool-2-thread-8	146	1
pool-2-thread-7	98	1
pool-2-thread-2	102	1
pool-2-thread-1	84	1
pool-2-thread-6	84	1
pool-2-thread-5	99	1
pool-2-thread-4	68	1
pool-2-thread-3	103	1
1000
1(预期的是10或者接近10,这个1是什么鬼???)

对SimpleDateFormat就是这么特殊

代码语言:javascript
复制
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("sdf1 = " + sdf1);
System.out.println("hashCode1 = " + sdf1.hashCode());
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("sdf2 = " + sdf2);
System.out.println("hashCode2 = " + sdf2.hashCode());
System.out.println(sdf1 == sdf2);
System.out.println(sdf1.equals(sdf2));
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-04-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ThreadLocal探索使用
    • 标签:多线程
    • 有坑版本
    • 无坑版本
    • why???
    • 对SimpleDateFormat就是这么特殊
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档