ThreadLocal 详解

转自:https://www.cnblogs.com/renyuanwei/p/9635235.html

什么是ThreadLocal

 

    根据JDK文档中的解释:ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。

    从这里可以看出,引入ThreadLocal的初衷是为了提供线程内的局部变量

 

ThreadLocal 不是一个线程,而是一个线程的本地化对象。当某个变量在使用 ThreadLocal 进行维护时,ThreadLocal 为使用该变量的每个线程分配了一个独立的变量副本。

每个线程可以自行操作自己对应的变量副本,而不会影响其他线程的变量副本。

 

API 方法

ThreadLocal 的 API 提供了如下的 4 个方法。

1)protected T initialValue()

返回当前线程的局部变量副本的变量初始值。


2)T get()

返回当前线程的局部变量副本的变量值,如果此变量副本不存在,则通过 initialValue() 方法创建此副本并返回初始值。


3)void set(T value)

设置当前线程的局部变量副本的变量值为指定值。


4)void remove()

删除当前线程的局部变量副本的变量值。


在实际使用中,我们一般都要重写 initialValue() 方法,设置一个特定的初始值。

关于initialValue的初始化。本人尝试了多种方式:

1
2
3
4
5
6
7
8
//new ThreadLocal方式:不推荐
        final  ThreadLocal<String> commandThreads =  new  ThreadLocal<String>() {
            @Override
            protected  String initialValue() {
                return  "execute :" +System.currentTimeMillis();
            }
        };
        System.out.println(commandThreads.get());

  

1
2
3
4
5
//withInitial方式:
         ThreadLocal<String> commandThreadnew =
//             ThreadLocal.withInitial(()-> "execute :"+System.currentTimeMillis());
                 ThreadLocal.withInitial(()-> new  String( "execute :" +System.currentTimeMillis()));
         System.out.println(commandThreadnew.get());

  

1
2
3
4
5
6
7
8
9
10
//(new Supplier<String>(){}方式 推荐
        ThreadLocal<String> commandThreadnew1 =
                ThreadLocal.withInitial( new  Supplier<String>() {
                    @Override
                    public  String get() {
                        return   "execute :" +System.currentTimeMillis();
                    }
                });
 
        System.out.println(  commandThreadnew1.get());

  

 

以下是关于ThreadLocal 解决多线程变量共享问题

存在争议点:

ThreadLocal到底能不能解决共享对象的多线程访问问题?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package  com.wuxianjiezh.demo.threadpool;
 
public  class  MainTest {
 
     public  static  void  main(String[] args) {
         Bank bank =  new  Bank();
         Thread xMThread =  new  Thread(() -> bank.deposit( 200 ),  "小明" );
         Thread xGThread =  new  Thread(() -> bank.deposit( 200 ),  "小刚" );
         Thread xHThread =  new  Thread(() -> bank.deposit( 200 ),  "小红" );
         xMThread.start();
         xGThread.start();
         xHThread.start();
     }
}
 
class  Bank {
 
     private  int  money =  1000 ;
 
     public  void  deposit( int  money) {
         String threadName = Thread.currentThread().getName();
         System.out.println(threadName +  "--当前账户余额为:"  this .money);
         this .money += money;
         System.out.println(threadName +  "--存入 "  + money +  " 后账户余额为:"  this .money);
         try  {
             Thread.sleep( 1000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
     }
}

  运行结果:存在多线程输出结果混乱

1
2
3
4
5
6
小明--当前账户余额为: 1000
小红--当前账户余额为: 1000
小红--存入  200  后账户余额为: 1400
小刚--当前账户余额为: 1000
小刚--存入  200  后账户余额为: 1600
小明--存入  200  后账户余额为: 1200

  

使用 ThreadLocal 保存对象的局部变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public  class  MainTest {
 
     public  static  void  main(String[] args) {
         Bank bank =  new  Bank();
         Thread xMThread =  new  Thread(() -> bank.deposit( 200 ),  "小明" );
         Thread xGThread =  new  Thread(() -> bank.deposit( 200 ),  "小刚" );
         Thread xHThread =  new  Thread(() -> bank.deposit( 200 ),  "小红" );
         xMThread.start();
         xGThread.start();
         xHThread.start();
     }
}
 
class  Bank {
 
     // 初始化账户余额为 100
     ThreadLocal<Integer> account = ThreadLocal.withInitial( new  Supplier<Integer>() {
         @Override
         public  Integer get() {
             return  1000 ;
         }
     });
 
     public  void  deposit( int  money) {
         String threadName = Thread.currentThread().getName();
         System.out.println(threadName +  "--当前账户余额为:"  + account.get());
         account.set(account.get() + money);
         System.out.println(threadName +  "--存入 "  + money +  " 后账户余额为:"  + account.get());
         try  {
             Thread.sleep( 1000 );
         catch  (InterruptedException e) {
             e.printStackTrace();
         }
     }
}

  运行结果为:

1
2
3
4
5
6
7
小明--当前账户余额为: 1000
小红--当前账户余额为: 1000
小红--存入  200  后账户余额为: 1200
小刚--当前账户余额为: 1000
小刚--存入  200  后账户余额为: 1200
小明--存入  200  后账户余额为: 1200
可以看到,我们要的效果达到了。各线程间同时操作自己的变量,相互间没有影响。

  

ThreadLocal 与 Thread 同步机制的比较

  • 同步机制采用了以时间换空间方式,通过对象锁保证在同一个时间,对于同一个实例对象,只有一个线程访问。

  • ThreadLocal 采用以空间换时间方式,为每一个线程都提供一份变量,各线程间同时访问互不影响。

转载于:https://www.cnblogs.com/sharpest/p/10884823.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值