ThreadLocal用法详解和原理

一、用法

ThreadLocal用于保存某个线程共享变量:对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。

1、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。

2、ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。

3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。

4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。

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

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

package com.coshaho.reflect;

 

/**

 * ThreadLocal用法

 * @author coshaho

 *

 */

public class MyThreadLocal

{

    private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>(){

        /**

         * ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值

         */

        @Override

        protected Object initialValue()

        {

            System.out.println("调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!");

            return null;

        }

    };

     

    public static void main(String[] args)

    {

        new Thread(new MyIntegerTask("IntegerTask1")).start();

        new Thread(new MyStringTask("StringTask1")).start();

        new Thread(new MyIntegerTask("IntegerTask2")).start();

        new Thread(new MyStringTask("StringTask2")).start();

    }

     

    public static class MyIntegerTask implements Runnable

    {

        private String name;

         

        MyIntegerTask(String name)

        {

            this.name = name;

        }

 

        @Override

        public void run()

        {

            for(int i = 0; i < 5; i++)

            {

                // ThreadLocal.get方法获取线程变量

                if(null == MyThreadLocal.threadLocal.get())

                {

                    // ThreadLocal.et方法设置线程变量

                    MyThreadLocal.threadLocal.set(0);

                    System.out.println("线程" + name + ": 0");

                }

                else

                {

                    int num = (Integer)MyThreadLocal.threadLocal.get();

                    MyThreadLocal.threadLocal.set(num + 1);

                    System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());

                    if(i == 3)

                    {

                        MyThreadLocal.threadLocal.remove();

                    }

                }

                try

                {

                    Thread.sleep(1000);

                }

                catch (InterruptedException e)

                {

                    e.printStackTrace();

                }

            }  

        }

         

    }

     

    public static class MyStringTask implements Runnable

    {

        private String name;

         

        MyStringTask(String name)

        {

            this.name = name;

        }

 

        @Override

        public void run()

        {

            for(int i = 0; i < 5; i++)

            {

                if(null == MyThreadLocal.threadLocal.get())

                {

                    MyThreadLocal.threadLocal.set("a");

                    System.out.println("线程" + name + ": a");

                }

                else

                {

                    String str = (String)MyThreadLocal.threadLocal.get();

                    MyThreadLocal.threadLocal.set(str + "a");

                    System.out.println("线程" + name + ": " + MyThreadLocal.threadLocal.get());

                }

                try

                {

                    Thread.sleep(800);

                }

                catch (InterruptedException e)

                {

                    e.printStackTrace();

                }

            }

        }

         

    }

<strong>}

</strong>

运行结果如下:

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!

线程IntegerTask1: 0

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!

线程IntegerTask2: 0

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!

线程StringTask1: a

线程StringTask2: a

线程StringTask1: aa

线程StringTask2: aa

线程IntegerTask1: 1

线程IntegerTask2: 1

线程StringTask1: aaa

线程StringTask2: aaa

线程IntegerTask2: 2

线程IntegerTask1: 2

线程StringTask2: aaaa

线程StringTask1: aaaa

线程IntegerTask2: 3

线程IntegerTask1: 3

线程StringTask1: aaaaa

线程StringTask2: aaaaa

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!

线程IntegerTask2: 0

调用get方法时,当前线程共享变量没有设置,调用initialValue获取默认值!

线程IntegerTask1: 0

二、原理

线程共享变量缓存如下:

Thread.ThreadLocalMap<ThreadLocalObject>;

1、Thread: 当前线程,可以通过Thread.currentThread()获取。

2、ThreadLocal:我们的static ThreadLocal变量。

3、Object: 当前线程共享变量。

我们调用ThreadLocal.get方法时,实际上是从当前线程中获取ThreadLocalMap<ThreadLocalObject>,然后根据当前ThreadLocal获取当前线程共享变量Object。

ThreadLocal.set,ThreadLocal.remove实际上是同样的道理。

 

这种存储结构的好处:

1、线程死去的时候,线程共享变量ThreadLocalMap则销毁。

2、ThreadLocalMap<ThreadLocal,Object>键值对数量为ThreadLocal的数量,一般来说ThreadLocal数量很少,相比在ThreadLocal中用Map<Thread, Object>键值对存储线程共享变量(Thread数量一般来说比ThreadLocal数量多),性能提高很多。

 

关于ThreadLocalMap<ThreadLocalObject>弱引用问题:

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<nullObject>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。

虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。

1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。

 

源码分析参考:

http://qifuguang.me/2015/09/02/[Java%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83]%E8%A7%A3%E5%AF%86ThreadLocal/

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
ThreadLocal是一种与线程绑定的变量,它可以解决多线程并发访问的问题。与Synchronized不同,ThreadLocal为每个线程提供了一个独立的变量副本,每个线程都可以独立地修改自己的副本,而不会影响其他线程的副本。 ThreadLocal使用方法比较简单。我们可以通过ThreadLocal类的set()方法来设置当前线程所关联的变量的值,通过get()方法来获取当前线程所关联的变量的值。在使用ThreadLocal后,如果不再需要这个变量,应该调用remove()方法来清除当前线程的关联变量,避免内存泄漏的问题。 ThreadLocal原理是通过每个线程都拥有一个独立的ThreadLocalMap对象来实现的。ThreadLocalMap内部使用一个Entry数组来存储键值对,键为ThreadLocal对象,值为对应的变量副本。在获取当前线程所关联的变量时,会根据ThreadLocal对象找到对应的变量副本并返回。ThreadLocal与Thread、ThreadLocalMap之间的关系是,每个线程都有一个ThreadLocalMap对象,其中存储了与该线程关联的所有ThreadLocal对象及其对应的变量副本。 ThreadLocal的常见使用场景包括但不限于: - 解决线程安全问题:可以将需要在多个线程中共享的数据存储在ThreadLocal变量中,每个线程访问自己的变量副本,避免了线程安全问题。 - 传递上下文信息:可以将一些需要在多个方法中共享的上下文信息存储在ThreadLocal变量中,在方法调用链中方便地获取这些上下文信息。 - 数据库连接管理:可以将数据库连接存储在ThreadLocal变量中,在每个线程中独立管理数据库连接,避免了线程间的冲突。 总之,ThreadLocal提供了一种方便的方式来实现线程间的数据隔离和传递,能有效地解决多线程并发访问的问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [史上最全ThreadLocal 详解](https://blog.csdn.net/qq_43842093/article/details/126715922)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [ThreadLocal详解](https://blog.csdn.net/m0_49508485/article/details/123234587)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值