美文网首页
两大使用场景-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