ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ThreadLocal

2022-01-19 21:00:32  阅读:171  来源: 互联网

标签:StringBuilder threadLocal ThreadLocal static new 线程


ThreadLocal 与 Thread 同步机制的比较

  • Thread同步机制采用了以时间换空间方式,通过对象锁保证在同一个时间,对于同一个实例对象,只有一个线程访问。
  • ThreadLocal 采用以空间换时间方式,为每一个线程都提供一份变量,各线程间同时访问互不影响。
 

定义ThreadLocal的同时为当前线程的局部变量副本赋初始值

☑ 方式1:ThreadLocal#withInitial(Supplier<? extends S> supplier) withInitial的参数是函数式接口Supplier<T>   private static ThreadLocal<StringBuilder> threadLocal = ThreadLocal.withInitial(StringBuilder::new)   ☑ 方式2:   private static ThreadLocal<StringBuilder> threadLocal = new ThreadLocal<StringBuilder>() {     @Override     protected StringBuilder initialValue() {         return new StringBuilder();     } };   zhenghe-common里 RedisDistributedLock#lockFlag   /** ThreadLocal保存当前工作线程里所添加的所有的锁及锁持有者的关系 */ private ThreadLocal<Map<String, String>> lockFlag = ThreadLocal.withInitial(Maps::newHashMap);    

ThreadLocal无法解决共享对象的更新问题

对于如下代码。 threadLocal里操作的StringBuilder与全局StringBuilder是同一个内存对象。 所以,在多线程往自己的ThreadLocal里的StringBuilder里append数据的时候,操作的都是全局的StringBuilder。 所以,这段代码定义的ThreadLocal<StringBuilder>没有任何意义。  因为StringBuilder是线程不安全的,所以,会出现线程不安全问题:某些并发场景下会出现有的线程没有append进去。          如果想实现线程安全,那么,不是用ThreadLocal,而是用线程安全的StringBuffer,或者借助线程同步锁。 static StringBuilder sb = new StringBuilder("init"); private static ThreadLocal<StringBuilder> threadLocal = ThreadLocal.withInitial(() -> sb);     所以说,最好不要用ThreadLocal来操作共享对象。 尽量仅让其持有当前线程里的对象。  

完整代码

package jstudy.threadlocal;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * {@link java.lang.ThreadLocal}无法解决共享对象的更新问题。本代码实例将证明这一点。
 * 结果虽然都append了,但是,是无序的
 * 所以,使用某个引用来操作共享对象时,依然需要进行线程同步
 */
@Slf4j
public class InitValueInThreadLocal {
    static StringBuilder sb = new StringBuilder("init");
    private static ThreadLocal<StringBuilder> threadLocal = ThreadLocal.withInitial(StringBuilder::new);

    static AtomicInteger integer = new AtomicInteger();

    public void print() {
        StringBuilder stringBuilder = threadLocal.get();
        int j = integer.getAndIncrement();
        log.info("初始:{}  val={}", stringBuilder.toString(), j);

        try {
            Thread.sleep(RandomUtils.nextInt(0, 5));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        stringBuilder.append("-" + j);

        threadLocal.remove();
        log.info(stringBuilder.toString());
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            InitValueInThreadLocal1 ins = new InitValueInThreadLocal1();
            new Thread(() -> ins.print()).start();
        }
        TimeUnit.SECONDS.sleep(2);
        log.info(sb.toString());
    }
}

由于StringBuilder线程不安全,如下是 意料之外的结果。即0没有append到sb对象里。

 

标签:StringBuilder,threadLocal,ThreadLocal,static,new,线程
来源: https://www.cnblogs.com/buguge/p/15824081.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有