1.7 ThreadLocal的原理和使用详解

目录

一、why:为什么要用ThreadLocal

1.1示例:不使用ThreadLocal,使用static,每个值修改同一个变量,产生错误

 1.2运行结果,可能有几种可能性:

 1.3使用ThreadLocal

1.4结果:都是2

二、ThreadLocal的使用

2.1ThreadLocal类接口很简单,常用的有4个方法

2.2remove的测试

2.3执行结果 

三、ThreadLocal初始化方法的使用

1、实现方法initialValue

2、通过lomoba表达式

四、ThreadLocal在线程中的使用,即怎么让多个线程使用threadLocal

1、线程对象内部自定义threadLocal

2、通过构造函数传递进线程的ThreadLocal变量


一、why:为什么要用ThreadLocal

1.1示例:不使用ThreadLocal,使用static,每个值修改同一个变量,产生错误

/**
 * 不使用ThreadLocal共同使用变量,使用static,每个值修改同一个变量,产生错误
 * @author: honry.guan
 * @create: 2020-06-07 18:53
 **/
public class NoThreadLocalTest extends Thread{
    private static int num = 1;
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num);
        num  = num + 1;
        System.out.println(Thread.currentThread().getName()+" 结束,num = "+num);
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

 1.2运行结果,可能有几种可能性:

Thread-1: 开始执行,num = 1
Thread-0: 开始执行,num = 1
Thread-0 结束,num = 3
Thread-2: 开始执行,num = 1
Thread-1 结束,num = 2
Thread-2 结束,num = 4

也有可能是:

Thread-1: 开始执行,num = 1
Thread-2: 开始执行,num = 1
Thread-2 结束,num = 2
Thread-0: 开始执行,num = 1
Thread-0 结束,num = 4
Thread-1 结束,num = 4

 1.3使用ThreadLocal

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

1.4结果:都是2

Thread-0: 开始执行,num = 1
Thread-2: 开始执行,num = 1
Thread-2 结束,num = 2
Thread-1: 开始执行,num = 1
Thread-0 结束,num = 2
Thread-1 结束,num = 2

 

二、ThreadLocal的使用

2.1ThreadLocal类接口很简单,常用的有4个方法

• void set(Object value):设置当前线程的线程局部变量的值。

• public Object get():该方法返回当前线程所对应的线程局部变量。

• public void remove():移除变量,如果最开始是1,通过set修改之后,调用remove之后,get出来的值又变成1

• protected Object initialValue():初始,第一次调用set或get才执行,并且仅执行一次。

2.2remove的测试

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
        num.remove();
        System.out.println(Thread.currentThread().getName()+" 结束remove之后,num = "+num.get());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

2.3执行结果 

Thread-1: 开始执行,num = 1
Thread-2: 开始执行,num = 1
Thread-0: 开始执行,num = 1
Thread-0 结束,num = 2
Thread-2 结束,num = 2
Thread-1 结束,num = 2
Thread-1 结束remove之后,num = 1
Thread-2 结束remove之后,num = 1
Thread-0 结束remove之后,num = 1

三、ThreadLocal初始化方法的使用

1、实现方法initialValue

private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
    @Override
    protected Integer initialValue() {
        return 1;
    }
};

2、通过lomoba表达式

ThreadLocal<Integer> num = ThreadLocal.withInitial(()->1);

四、ThreadLocal在线程中的使用,即怎么让多个线程使用threadLocal

(我刚学的时候一直被这个事情困扰,自己手写几次之后明白)

只要在线程run方法中,调用ThreadLocal的get或者set,不管是线程对象内部自定义threadLocal,还是通过构造函数传递进线程的ThreadLocal变量。

 

1、线程对象内部自定义threadLocal

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
        num.remove();
        System.out.println(Thread.currentThread().getName()+" 结束remove之后,num = "+num.get());
    }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            NoThreadLocalTest t = new NoThreadLocalTest();
            t.start();
        }
    }
}

2、通过构造函数传递进线程的ThreadLocal变量

public class NoThreadLocalTest extends Thread{
    private ThreadLocal<Integer> num ;

    public NoThreadLocalTest(ThreadLocal<Integer> num) {
        this.num = num;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
        num.set(num.get() + 1);
        System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
        num.remove();
        System.out.println(Thread.currentThread().getName()+" 结束remove之后,num = "+num.get());
    }

    public static void main(String[] args) {
        ThreadLocal<Integer> num = ThreadLocal.withInitial(()->1);
    }
}

此时不论什么一个线程能够并发访问这个变量,对它进行写入、读取操作,都是线程安全的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值