提出问题
在 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<Integer> threadLocal = new ThreadLocal();
放数据
threadLocal.set(值)
取数据
threadLocal.get()
移除数据
remove()
例子-解决上面bug
package ThreadLocal;
public class Std1 implements Runnable{
/**
* 创建 ThreadLocal 对象
*/
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
每个线程操作本线程的变量,所以正确执行