java 多线程 - ThreadLocal

提出问题

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

每个线程操作本线程的变量,所以正确执行

参考:
https://www.cnblogs.com/chanshuyi/p/5286637.html


原文出处:http://malaoshi.top/show_1IX4sEFhd7Fz.html