提出问题
在 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
分析
2个线程,同时执行 i++ 自增1,导致跳过一个数字
解决
使用 ThreadLocal 将变量 i 定义成 线程局部变量
ThreadLocal 说明
ThreadLocal 是创建线程 局部变量 的类
一般,在 Thread 子类、Runnable 接口实现类,创建的变量是可以被 所有线程访问并修改的
而使用 ThreadLocal 创建的变量只能被 当前线程 访问,其他线程则无法访问和修改
使用
创建对象
关键: ThreadLocal 声明为 static 静态变量
private static ThreadLocal<Integer> 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<Integer> 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

