java 多线程 - ThreadLocal 作者:马育民 • 2023-01-30 16:13 • 阅读:10065 # 提出问题 在 `Runnable` 实现类中,定义成员变量 `i`,在多线程中访问并修改改变量,不做额外处理,导致执行结果不正确 ``` package ThreadLocal; public class bug所有线程都能访问修改变量 implements Runnable{ /** * 成员变量 i,在多线程中访问并修改改变量 * 由于没有做额外处理,所有线程都访问、修改此变量 */ int i; @Override public void run() { while (i<=20) { System.out.println(Thread.currentThread().getName()+",i="+i); i++; try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) { bug所有线程都能访问修改变量 std1 = new bug所有线程都能访问修改变量(); Thread t1 = new Thread(std1,"线程1"); Thread t2 = new Thread(std1,"线程2"); t1.start(); t2.start(); } } ``` 执行结果如下: ``` 线程1,i=0 线程2,i=0 线程1,i=2 // 由于2个线程都执行了自增1,导致跳过了1 线程2,i=2 线程1,i=4 // 由于2个线程都执行了自增1,导致跳过了3 线程2,i=4 线程1,i=6 线程2,i=6 ``` ### 分析 [](/upload/0/0/1IX4sE22mOy0.png) 2个线程,同时执行 `i++` 自增1,导致跳过一个数字 ### 解决 使用 `ThreadLocal` 将变量 `i` 定义成 **线程局部变量** # ThreadLocal 说明 `ThreadLocal` 是创建线程 **局部变量** 的类 一般,在 `Thread` 子类、`Runnable` 接口实现类,创建的变量是可以被 **所有线程访问并修改**的 而使用 `ThreadLocal` 创建的变量只能被 **当前线程** 访问,**其他线程则无法访问和修改** # 使用 [](/upload/0/0/1IX4sEFVWSJt.png) ### 创建对象 **关键:** `ThreadLocal` 声明为 `static` 静态变量 ``` private static ThreadLocal threadLocal = new ThreadLocal(); ``` ### 推荐 static 静态变量 一般定义成 **static静态** `ThreadLocal` 变量 **原因:** 为了 **避免重复创建** **TSO**(thread specific object,即与线程相关的变量) 不同的线程,`ThreadLocalMap` 是隔离开的,互相不会影响。所以,设置为 `static` 静态变量,其 TSO 也是隔离的,不会影响 如果不加 **static**,`ThreadLocal` 变量是 **某个类的实例变量**,那么每创建一个该类的对象,就会创建一个的 `ThreadLocal` 对象,**会浪费内存资源** 所以建议ThreadLocal用 `static` 修饰 参考: https://blog.csdn.net/xiaochao_bos/article/details/103469733 https://blog.csdn.net/u013543848/article/details/102980066 ### 放数据 ``` threadLocal.set(值) ``` ### 取数据 ``` threadLocal.get() ``` ### 移除数据 ``` remove() ``` # 例子-解决上面bug ``` package ThreadLocal; public class Std1 implements Runnable{ /** * 创建 ThreadLocal 对象 */ private static ThreadLocal threadLocal = new ThreadLocal(); @Override public void run() { // 先取值 Integer i = threadLocal.get(); // 如果为null,就赋默认值值 if (i == null) { i=1; } while (i<=20) { System.out.println(Thread.currentThread().getName()+",i="+i); i++; // 将数据放入到 ThreadLocal 对象中 threadLocal.set(i); try { Thread.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } } } public static void main(String[] args) { Std1 std1 = new Std1(); Thread t1 = new Thread(std1,"线程1"); Thread t2 = new Thread(std1,"线程2"); t1.start(); t2.start(); } } ``` ### 执行结果 ``` 线程1,i=1 线程2,i=1 线程1,i=2 线程2,i=2 ``` 每个线程操作本线程的变量,所以正确执行 参考: https://www.cnblogs.com/chanshuyi/p/5286637.html 原文出处:http://malaoshi.top/show_1IX4sEFhd7Fz.html