【ThreadLocal】ThreadLocal使用场景及示例

ThreadLocal是线程封闭的重要措施,Spring中DateTimeContextHolder和RequestContextHolder也有用到。

ThreadLocal的使用场景

场景1:对象隔离–线程需要一个独享的对象(例如SimpleDateFormat)

线程独享一个对象工具类,例如Random、DateFormat,通常出于线程安全、提高效率两方面的考虑。

对于线程独享对象场景,主要是重写innitialValue()方法。

public class RrightWaySimpleDateFormater {
    
    
    public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
    /**
     * 定义ThreadLocal变量--JDK8实现形式
     */
    private static final ThreadLocal<SimpleDateFormat> dateThreadSafe = ThreadLocal.withInitial(
        () -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
   );

    public static void main(String[] args) throws InterruptedException {
    
    
        for (int i = 0; i < 1000; i++) {
    
    
            int finalI = i;
            threadPool.submit(() -> {
    
    
                System.out.println(new RrightWaySimpleDateFormater().date(finalI));
            });
        }
        threadPool.shutdown();
    }

    public String date(int seconds) {
    
    
        return dateThreadSafe.get().format(new Date(1000 * seconds));
    }
}

JDK7以及之前的实现形式

private static final ThreadLocal<SimpleDateFormat> dateThreadSafe2 = new ThreadLocal<SimpleDateFormat>() {
    
    
     @Override
     protected SimpleDateFormat initialValue() {
    
    
         return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
     }
};

需要注意的是,ThreadLocal通常不适合线程池。

阿里java手册:
【强制】SimpleDateFormat是线程不安全的类,一般不要定义为static变量。如果定义为static,必须加锁,或使用DateUtils工具类。亦推荐上述处理。
JDK8推荐使用DateTimeFormatter代替SimpleDateFormat,Instant代替Date,LocalDateTime代替Calendar。

场景2:对象传递

线程需要保存全局变量,可以让不同的方法直接使用,而不需要让数据作为参数层层传递。

ThreadLocal可以用于保存一些业务信息,例如用户权限信息、用户名、userId、手机号等等。这些业务信息在同一个线程中相同,不同线程中内容不同。强调的是同一个请求内(同一个线程内)不同方法间的共享。

Map也可以存储上述业务信息。多线程同时工作时,需要保证线程安全。例如,采用静态ConcurrentHashMap变量,将线程ID作为key,业务数据作为Value保存,可以做到线程间隔离,但仍有性能影响。

public class RightWayThreadLocalUse {
    
    
    public static void main(String[] args) {
    
    
        new ServiceImpl1().service(new UserInfo("lzp", "1234567890"));
    }
}

/**
 * 接收数据
 */
class ServiceImpl1 {
    
    
    public void service(UserInfo userInfo) {
    
    
        UserInfoHolder.holder.set(userInfo);
        new ServiceImpl2().service();
    }
}

/**
 * 处理数据1
 */
class ServiceImpl2 {
    
    
    public void service() {
    
    
        System.out.println("客户名:" + UserInfoHolder.holder.get().cltNam);
        new ServiceImpl3().service();
    }
}

/**
 * 处理数据2
 */
class ServiceImpl3 {
    
    
    public void service() {
    
    
        System.out.println("客户号:" + UserInfoHolder.holder.get().cltNbr);
        // 此时使用完ThreadLocal,回收该ThreadLocal
        UserInfoHolder.holder.remove();
    }
}

class UserInfoHolder {
    
    
    public static ThreadLocal<UserInfo> holder = new ThreadLocal<>();
}

class UserInfo {
    
    
    String cltNam;
    String cltNbr;

    public UserInfo(String cltNam, String cltNbr) {
    
    
        this.cltNam = cltNam;
        this.cltNbr = cltNbr;
    }
}

ThreadLocal的优势

(1)ThreadLocal变量线程私有,因此线程安全,不需要加锁,从而没有阻塞,提高了执行效率。
(2)高效利用内存,节省开销。对于使用线程池的场景,只需要每个woker线程拥有一个该对象的ThreadLocal实例即可。
(3)对于调用链场景,避免对象的反复传递,用于存储业务信息,实现代码解耦。

猜你喜欢

转载自blog.csdn.net/LIZHONGPING00/article/details/105211631
今日推荐