美文网首页
两大使用场景-ThreadLocal 的用途

两大使用场景-ThreadLocal 的用途

作者: qyfl | 来源:发表于2020-01-07 22:14 被阅读0次

    典型场景一:

    每个线程需要一个独享的对象(通常是工具类,典型的类有 SimpleDateFormat 和 Random)

    代码示例:

    public class ThreadLocalNormalUsage05 {
    
        public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
    
        public static void main(String[] args) {
    
            for (int i = 0; i < 1000; i++) {
                int finalI = i;
                threadPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        String date = new ThreadLocalNormalUsage05().date(finalI);
                        System.out.println(date);
                    }
                });
            }
        }
    
        public String date(int secods) {
            Date date = new Date(1000 * secods);
            SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal.get();
            return dateFormat.format(date);
        }
    }
    
    class ThreadSafeFormatter {
        public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>(){
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            }
        };
    }
    

    典型场景二:

    每个线程内需要保存全局变量(例如在连接器中获取用户信息),可以让不同方法直接使用,避免参数传递的麻烦。

    这些信息在同一个线程内相同,但是在不同的线程内是不同的,所以不能使用 static 方法来解决。使用 ConcurrentHashMap,synchronized 等会对性能有影响。

    代码示例:

    
    public class ThreadLocalNormalUsage06 {
        public static void main(String[] args) {
            new Service1().process();
        }
    }
    
    
    class Service1 {
        public void process() {
            User user = new User("小张");
            UserContextHolder.holder.set(user);
            new Service2().process();
        }
    }
    
    class Service2 {
        public void process() {
            User user = UserContextHolder.holder.get();
            System.out.println("service2" + user.name);
            new Service3().process();
        }
    }
    
    class Service3 {
        public void process() {
            User user = UserContextHolder.holder.get();
            System.out.println("service3" + user.name);
            UserContextHolder.holder.remove();
        }
    }
    
    class UserContextHolder {
        public static ThreadLocal<User> holder = new ThreadLocal<>();
    }
    
    class User {
        String name;
    
        public User(String name) {
            this.name = name;
        }
    }
    

    总结

    ThreadLocal 有 set 和 initialValue 两种方法保存对象,当我们能够控制对象初始化时机的时候可以用 initialValue。当我们控制不了,需要某些条件触发的时候可用 set。

    使用 ThreadLocal 的四点好处:

    • 线程安全
    • 不需要加锁,提高执行效率
    • 高效使用内存(对象生成),节省开销。
    • 免去传参的麻烦,如场景二。

    每一个线程都有一个 ThreadLocalMap,这里面存放的是 ThreadLocal,如果在使用线程池的环境下,不停的向 ThreadLocalMap 中新增 ThreadLocal,那么极有可能引发内存泄露,所以在最后调用完的时候需要记得删除ThreadLocal。如 Service3 中的示例。

    相关文章

      网友评论

          本文标题:两大使用场景-ThreadLocal 的用途

          本文链接:https://www.haomeiwen.com/subject/movdactx.html