Android多线程-----并发和同步(ThreadLocal)

一.对ThreadLocal的理解

       很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量,也就是进行数据隔离。

       在看很多博客的时候,总是有很多作者在描述ThreadLocal的作用是数据隔离,并且是每一个线程复制了一份,每个线程的访问的数据都是不受其他线程影响的。其实,这句话前半句是对的,ThreadLocal的确是数据的隔离,但是并非数据的复制(准确的说是声明和初始化的),而是在每一个线程中创建一个新的数据对象,然后每一个线程使用的是不一样的。

       说白了,ThreadLocal就是想在多线程环境下去保证成员变量的安全。就是解决对线程访问共享资源时发生冲突的问题,也算是一种同步方式

二、先了解一下ThreadLocal类提供的几个方法

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

set需要首先获得当前线程对象Thread;

然后取出当前线程对象的成员变量ThreadLocalMap;注意,变量是保存在线程中的,而不是保存在ThreadLocal变量中

如果ThreadLocalMap存在,那么进行KEY/VALUE设置,KEY就是ThreadLocal;

如果ThreadLocalMap没有,那么创建一个;

说白了,当前线程中存在一个Map变量,KEY是ThreadLocal,VALUE是你设置的值。

2、ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。每个线程的变量副本是存储在哪里的?

每个线程都有一个这样的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal对象声明的变量类型作为参数。这样,我们所使用的ThreadLocal变量的实际数据,通过get函数取值的时候,就是通过取出Thread中threadLocals引用的map,然后从这个map中根据当前threadLocal作为参数,取出数据。现在,变量的副本从哪里取出来的已经确认解决了。

3、ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
4、ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值。

我们先来看一个例子

运行结果

是你想象中的结果么?

很显然,在这里,并没有通过ThreadLocal达到线程隔离的机制,可是ThreadLocal不是保证线程安全的么?这是什么鬼?

虽然,ThreadLocal让访问某个变量的线程都拥有自己的局部变量,但是如果这个局部变量都指向同一个对象呢?这个时候ThreadLocal就失效了。仔细观察下图中的代码,你会发现,threadLocal在初始化时返回的都是同一个对象a!
 

再来看一个例子

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方法时,当前线程共享变量没有设置,获取默认值!");

            return null;

        }

    };

     

    public static void main(String[] args)

    {

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

        new Thread(new MyIntegerTask("IntegerTask2")).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();

                }

            }  

        }

         

    }

运行结果如下:

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

线程IntegerTask1: 0

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

线程IntegerTask2: 0

线程IntegerTask1: 1

线程IntegerTask2: 1

线程IntegerTask2: 2

线程IntegerTask1: 2

线程IntegerTask2: 3

线程IntegerTask1: 3

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

线程IntegerTask2: 0

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

线程IntegerTask1: 0

 

三.ThreadLocal与其他同步机制的区别

       ThreadLocal与普通的同步机制都是为了解决多线程访问共享资源时会产生冲突的问题,
       普通的同步机制是控制了线程对共享资源的访问时间而避免冲突的,他是多个线程进行通信的有效方式,
       而ThreadLocal则是在空间上对共享数据进行了隔离,从根本上来说,数据已经不在共享了以此避免冲突。因此两种方式是在不同的角度所实现的线程安全。
       当我们需要多线程之间进行通信就使用同步机制,需要隔离多个线程之间的共相冲突,就是用ThreadLocal。

四.ThreadLocal的应用场景

  最常见的ThreadLocal使用场景为 用来解决 数据库连接、Session管理等。

感谢:
http://www.importnew.com/20963.html
http://www.cnblogs.com/dolphin0520/p/3920407.html
https://www.cnblogs.com/coshaho/p/5127135.html

猜你喜欢

转载自blog.csdn.net/pangjl1982/article/details/85014715