ThreadLocal极其应用

一、概念

1.1 什么是ThreadLocal?

ThreadLocal是线程局部变量,所谓的线程局部变量,就是仅仅只能被本线程访问,不能在线程之间进行共享访问的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

1.2 问题引入

假如语文老师有一本书,但是班上有30名学生,老师将这本书送给学生们去阅读,30名学生都想阅读这本书。

为保证每个学生都能阅读到书籍,那么基本可以有两种方案,一是按照某种排序(例如姓名首字母排序),让每个学生依次阅读。

二是让30名学生同时争抢,谁抢到谁就去阅读,读完放回原处,剩下的29名学生再次争抢。

显然第一种方案,基本表现为串行阅读,时间成本较大,第二种方案为多个学生争抢,容易发生安全问题(学生发生冲突或者书籍在争抢过程中被毁坏)。

为了解决这两个问题,那么有没有更加好的方案呢?当然有,老师可以将书籍复印30本,每个学生都发一本,这样既大大提高了阅读效率,节约了阅读时间,还能保证每个学生都能有自己的书籍,这样就不会发生争抢,避免了安全问题。

1.3 类似案例

假如我们有一个需求,那就是在多线程环境下,去格式化时间为指定格式yyyy-MM-dd HH:mm:ss,假设一开始只有两个线程需要这么做,代码如下:

public class Test {
    
    public static void main(String[] args) {
        
        new Thread( ()-> System.out.println(  new Test().date( 10 ) ) ).start();
        
        new Thread( ()-> System.out.println(  new Test().date( 100 ) ) ).start();
        
        
    }

    private String date(int seconds) {
        // 参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
        Date date = new Date(1000 * seconds);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFormat.format(date);
    }
}

在线程少的情况下是没有问题的,我们在每个线程里调用date方法,也就是在每个线程里都执行了创建SimpleDateFormat对象,每个对象在各自的线程里面执行格式化时间。但是我们是否会思考到,假如有1000个线程需要格式化时间,那么需要调用1000次date方法,也就是需要创建1000个作用一样的SimpleDateFormat对象,这样是不是太浪费内存了?也给GC带来压力?

于是我们联想到,1000个线程来共享一个SimpleDateFormat对象,这样SimpleDateFormat对象只需要创建一次即可,代码如下:

public class Test2 {
//    创建一个定长线程池,并发数量控制在10
    public static  ExecutorService executorService = Executors.newFixedThreadPool(10);
    private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        for (int index = 0; index < 1000 ; index++) {
            int finalIndex = index;
            executorService.submit( () -> System.out.println( new Test2().date(finalIndex) ) );
        }
        // 关闭线程池,此种关闭方式不再接受新的任务提交,等待现有队列中的任务全部执行完毕之后关闭
        executorService.shutdown();
    }
    private String date(int seconds) {
        // 参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
        Date date = new Date(1000 * seconds);
        return DATE_FORMAT.format(date);
    }
}

上述代码我们使用到了固定线程数的线程池来执行时间格式化任务,我们来执行一下,看看结果:

猜你喜欢

转载自blog.csdn.net/jatej/article/details/107601305