精通高并发与多线程,却不会用ThreadLocal?

作者:蔡不菜丶链接:来源:掘金
之前我们有在并发系列中提到 ThreadLocal 类和基本使用方法 , 那我们就来看下 ThreadLocal 究竟是如何使用的!
ThreadLocal 简介概念ThreadLocal 类是用来提供线程内部的局部变量 。 这种变量在多线程环境下访问(get 和 set 方法访问)时能保证各个线程的变量相对独立于其他线程内的变量 。ThreadLocal 实例通常来说都是 private static 类型的 , 用于关联线程和上下文 。
作用

  • 传递数据
提供线程内部的局部变量 。 可以通过 ThreadLocal 在同一线程 , 不同组件中传递公共变量 。
  • 线程并发
适用于多线程并发情况下 。
  • 线程隔离
每个线程的变量都是独立的 , 不会相互影响 。
ThreadLocal 实战1. 常见方法
  • ThreadLocal ()
构造方法 , 创建一个 ThreadLocal 对象
  • void set (T value)
设置当前线程绑定的局部变量
  • T get ()
获取当前线程绑定的局部变量
  • void remove ()
移除当前线程绑定的局部变量
2. 为什么要使用 ThreadLocal首先我们先看一组并发条件下的代码场景:
@Datapublic class ThreadLocalTest {private String name;public static void main(String[] args) {ThreadLocalTest tmp = new ThreadLocalTest();for (int i = 0; i < 4; i++) {Thread thread = new Thread(() -> {tmp.setName(Thread.currentThread().getName());System.out.println(Thread.currentThread().getName() +"\t 拿到数据:" + tmp.getName());});thread.setName("Thread-" + i);thread.start();}}}复制代码我们理想中的代码输出结果应该是这样的:
/** OUTPUT **/Thread-0拿到数据:Thread-0Thread-1拿到数据:Thread-1Thread-2拿到数据:Thread-2Thread-3拿到数据:Thread-3复制代码但是实际上输出的结果却是这样的:
/** OUTPUT **/Thread-0拿到数据:Thread-1Thread-3拿到数据:Thread-3Thread-1拿到数据:Thread-1Thread-2拿到数据:Thread-2复制代码顺序乱了没有关系 , 但是我们可以看到 Thread-0 这个线程拿到的值却是 Thread-1
从结果中我们可以看出多个线程在访问同一个变量的时候会出现异常 , 这是因为线程间的数据没有隔离!
并发线程出现的问题?那加锁不就完事了!这个时候你三下五除二的写下了以下代码:
@Datapublic class ThreadLocalTest {private String name;public static void main(String[] args) {ThreadLocalTest tmp = new ThreadLocalTest();for (int i = 0; i < 4; i++) {Thread thread = new Thread(() -> {synchronized (tmp) {tmp.setName(Thread.currentThread().getName());System.out.println(Thread.currentThread().getName()+ "\t" + tmp.getName());}});thread.setName("Thread-" + i);thread.start();}}}/** OUTPUT **/Thread-2 Thread-2Thread-3 Thread-3Thread-1 Thread-1Thread-0 Thread-0复制代码从结果上看 , 加锁好像是解决了上述问题 , 但是 synchronized 常用于多线程数据共享的问题 , 而非多线程数据隔离的问题 。 这里使用 synchronized 虽然解决了问题 , 但是多少有些不合适 , 并且 synchronized 属于重量级锁 , 为了实现多线程数据隔离贸然的加上 synchronized , 也会影响到性能 。
加锁的方法也被否定了 , 那么该如何解决?不如用 ThreadLocal 牛刀小试一番:
public class ThreadLocalTest {private static ThreadLocal threadLocal = new ThreadLocal<>();public String getName() {return threadLocal.get();}public void setName(String name) {threadLocal.set(name);}public static void main(String[] args) {ThreadLocalTest tmp = new ThreadLocalTest();for (int i = 0; i < 4; i++) {Thread thread = new Thread(() -> {tmp.setName(Thread.currentThread().getName());System.out.println(Thread.currentThread().getName() +"\t 拿到数据:" + tmp.getName());});thread.setName("Thread-" + i);thread.start();}}}复制代码在查看输出结果之前 , 我们先来看看代码发生了哪些变化
【精通高并发与多线程,却不会用ThreadLocal?】首先多了一个 private static 修饰的 ThreadLocal, 然后在 setName 的时候 , 我们实际上是往 ThreadLocal 里面存数据 , 在 getName 的时候 , 我们是在 ThreadLocal 里面取数据 。 感觉操作上也是挺简单的 , 但是这样真的能做到线程间的数据隔离吗 , 我们再来看一看结果:
/** OUTPUT **/Thread-1拿到数据:Thread-1Thread-2拿到数据:Thread-2Thread-0拿到数据:Thread-0Thread-3拿到数据:Thread-3复制代码从结果上可以看到每个线程都能取到对应的数据 。 ThreadLocal 也已经解决了多线程之间数据隔离的问题 。
那么我们来小结一下 , 为什么需要使用ThreadLocal , 与 synchronized 的区别是什么
  • synchronized
原理: 同步机制采用 "以时间换空间" 的方式 , 只提供了一份变量 , 让不同线程排队访问