Java并发06---ThreadLocal类以及应用技巧
2018-03-31 16:19
561 查看
Java并发06---ThreadLocal类以及应用技巧
上一节总结了一下,线程范围内的数据共享问题,即定义一个Map,将当前线程名称和线程中的数据以键值对的形式存到Map中,然后在当前线程中使用数据的时候就可以根据当前线程名称从Map中拿到当前线程中的数据,这样就可以做到不同线程之间数据互不干扰。其实ThreadLocal类就是给我们提供了这个解决方法,所以我们完全可以用ThreadLocal来完成线程范围内数据的共享。
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
结合上一节的代码,可以看出,其实ThreadLocal就相当于一个Map,只不过我们不需要设定key了,它默认就是当前的Thread,往里面放数据,直接set即可,取数据,直接get即可,很方便,就不用Map一个个存了,但是问题来了,ThreadLocal虽然存取方便,但是get()方法中根本没有参数,也就是说我们只能往ThreadLocal中放一个数据,多了就不行了,那么该如何解决这个问题呢?
很明显,ThreadLocal是个容器,且只能存一下,那么如果有多个数据,我们可以定义一个类,把数据都封装到这个类中,然后扔到ThreadLocal中,用的时候取这个类,再从类中去我们想要的数据即可。
好,现在有两个线程,每个线程都要操作各自的数据,而且数据有两个:名字和年龄。根据上面的思路,写一个demo,如下:
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
这样进行一下封装就可以实现多个数据的存储了,但是上面这个程序是不太好的,原因很明显,在线程中,我要自己new一个对象,然后对其进行操作,最后还得把这个对象扔到当前线程中。这不太符合设计的思路,设计的思路应该是这样的,不能让用户自己去new啊,如果有个类似于
这让我想到了学习JDBC的时候,从ThreadLocal中拿connection时的做法了,如果当前ThreadLocal中有就拿出来,没有就产生一个,这跟这里的需求是一样的,我想要一个User,那我应该用User去调用
2
3
4
5
6
7
8
9
10
11
这是懒汉式单例模式的代码结构,我门完全可以效仿该思路去设计一个从当前线程中拿User的办法,所以将程序修改如下:
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
经过这样的改造,代码就优雅多了,外界从来不要考虑如何去当前线程中拿数据,只要拿就行,拿出来的肯定就是当前线程中你想要的对象,因为在对象内部已经写好了这个静态方法了,而且拿出来操作完了后,也不需要再放到threadLocal中,因为它本来就在threadLocal中,这就封装的相当好了。
ThreadLocal类的应用和使用技巧就总结这么多吧~
上一节总结了一下,线程范围内的数据共享问题,即定义一个Map,将当前线程名称和线程中的数据以键值对的形式存到Map中,然后在当前线程中使用数据的时候就可以根据当前线程名称从Map中拿到当前线程中的数据,这样就可以做到不同线程之间数据互不干扰。其实ThreadLocal类就是给我们提供了这个解决方法,所以我们完全可以用ThreadLocal来完成线程范围内数据的共享。
public class ThreadScopeShareData { //定义一个ThreadLocal private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(); public static void main(String[] args) { for(int i = 0; i < 2; i ++) { new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put a data: " + data); threadLocal.set(data);//直接往threadLocal里面里面扔数据即可 new TestA().getData(); new TestB().getData(); } }).start(); } } static class TestA { public void getData() { System.out.println("A get data from " + Thread.currentThread().getName() + ": " + threadLocal.get());//直接取,不用什么关键字,它直接从当前线程中取 } } static class TestB { public void getData() { System.out.println("B get data from " + Thread.currentThread().getName() + ": " + threadLocal.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
结合上一节的代码,可以看出,其实ThreadLocal就相当于一个Map,只不过我们不需要设定key了,它默认就是当前的Thread,往里面放数据,直接set即可,取数据,直接get即可,很方便,就不用Map一个个存了,但是问题来了,ThreadLocal虽然存取方便,但是get()方法中根本没有参数,也就是说我们只能往ThreadLocal中放一个数据,多了就不行了,那么该如何解决这个问题呢?
很明显,ThreadLocal是个容器,且只能存一下,那么如果有多个数据,我们可以定义一个类,把数据都封装到这个类中,然后扔到ThreadLocal中,用的时候取这个类,再从类中去我们想要的数据即可。
好,现在有两个线程,每个线程都要操作各自的数据,而且数据有两个:名字和年龄。根据上面的思路,写一个demo,如下:
public class ThreadScopeShareData { private static ThreadLocal<User> threadLocal = new ThreadLocal<User>(); public static void main(String[] args) { for(int i = 0; i < 2; i ++) {//开启两个线程 new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put a data: " + data); //每个线程中维护一个User,User中保存了name和age User user = new User(); user.setName("name" + data); user.setAge(data); threadLocal.set(user); //向当前线程中存入user对象 new TestA().getData(); new TestB().getData(); } }).start(); } } static class TestA { public void getData() { User user = threadLocal.get();//从当前线程中取出user对象 System.out.println("A get data from " + Thread.currentThread().getName() + ": " + user.getName() + "," + user.getAge()); } } static class TestB { public void getData() { User user = threadLocal.get();//从当前线程中取出user对象 System.out.println("B get data from " + Thread.currentThread().getName() + ": " + user.getName() + "," + user.getAge()); } } } //定义一个User类来存储姓名和年龄 class User { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }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
这样进行一下封装就可以实现多个数据的存储了,但是上面这个程序是不太好的,原因很明显,在线程中,我要自己new一个对象,然后对其进行操作,最后还得把这个对象扔到当前线程中。这不太符合设计的思路,设计的思路应该是这样的,不能让用户自己去new啊,如果有个类似于
getThreadInstance()的方法,用户想要从ThreadLocal中拿什么对象就用该对象去调用这个
getThreadInstance()方法多好,这样拿到的永远都是本线程范围内的对象了。
这让我想到了学习JDBC的时候,从ThreadLocal中拿connection时的做法了,如果当前ThreadLocal中有就拿出来,没有就产生一个,这跟这里的需求是一样的,我想要一个User,那我应该用User去调用
getThreadLInstance()方法获取本线程中的一个User对象,如果有就拿,如果没有就产生一个。完全一样的思路。这个设计跟单例的模式有点像,这里说有点像不是本质上像,是代码结构很像。先看一下简单的单例模式代码结构:
public class Singleton { private static Singleton instance = null; private Singleton() {//私有构造方法阻止外界new } public static synchronized Singleton getInstance() { //提供一个公共方法返回给外界一个单例的实例 if (instance == null) { //如果没有实例 instance = new Singleton(); //就新new一个 } return instance; //返回该实例 } }1
2
3
4
5
6
7
8
9
10
11
这是懒汉式单例模式的代码结构,我门完全可以效仿该思路去设计一个从当前线程中拿User的办法,所以将程序修改如下:
public class ThreadScopeShareData { //不需要在外面定义threadLocal了,放到User类中了 // private static ThreadLocal<User> threadLocal = new ThreadLocal<User>(); public static void main(String[] args) { for(int i = 0; i < 2; i ++) { new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put a data: " + data); //这里直接用User去调用getThreadLocal这个静态方法获取本线程范围内的一个User对象 //这里就优雅多了,我完全不用关心如何去拿该线程中的对象,如何把对象放到threadLocal中 //我只要拿就行,而且拿出来的肯定就是当前线程中的对象,原因看下面User类中的设计 User.getThreadInstance().setName("name" + data); User.getThreadInstance().setAge(data); new TestA().getData(); new TestB().getData(); } }).start(); } } static class TestA { public void getData() { //还是调用这个静态方法拿,因为刚刚已经拿过一次了,threadLocal中已经有了 User user = User.getThreadInstance(); System.out.println("A get data from " + Thread.currentThread().getName() + ": " + user.getName() + "," + user.getAge()); } } static class TestB { public void getData() { User user = User.getThreadInstance(); System.out.println("A get data from " + Thread.currentThread().getName() + ": " + user.getName() + "," + user.getAge()); } } } class User { private User() {} private static ThreadLocal<User> threadLocal = new ThreadLocal<User>(); //注意,这不是单例,每个线程都可以new,所以不用synchronized, //但是每个threadLocal中是单例的,因为有了的话就不会再new了 public static /*synchronized*/ User getThreadInstance() { User instance = threadLocal.get(); //先从当前threadLocal中拿 if(instance == null) { instance = new User(); threadLocal.set(instance);//如果没有就新new一个放到threadLocal中 } return instance; //向外返回该User } private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }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
经过这样的改造,代码就优雅多了,外界从来不要考虑如何去当前线程中拿数据,只要拿就行,拿出来的肯定就是当前线程中你想要的对象,因为在对象内部已经写好了这个静态方法了,而且拿出来操作完了后,也不需要再放到threadLocal中,因为它本来就在threadLocal中,这就封装的相当好了。
ThreadLocal类的应用和使用技巧就总结这么多吧~
相关文章推荐
- Java并发06---ThreadLocal类以及应用技巧
- 【java并发】ThreadLocal类以及应用技巧
- 【Java多线程与并发库】6.ThreadLocal类及应用技巧
- 【Java多线程与并发库】6.ThreadLocal类及应用技巧
- 06_张孝祥_多线程_ThreadLocal类及应用技巧
- Java并发28:ThreadLocal学习笔记-简介、基本方法及应用场景
- Java文件锁以及并发读写中的应用
- (7)java5线程并发库的应用(线程池) 以及在实际项目中的使用。。
- ThreadLocal类以及应用技巧
- 【张孝祥并发课程笔记】05:ThreadLocal类及应用技巧
- Java线程与并发库高级应用-线程范围内共享数据ThreadLocal类
- Java语言的出现背景、主要特点、发展历程以及Java技术的应用
- JDK 1.5 使用java.util.concurrent 开发并发应用
- java 调 dll 动态连接库以及java回调函数在dll中的应用
- Java应用技巧:SE6调用编译器的两种方法(1)
- Java应用技巧——为JTabbedPane添加关闭按钮
- Java应用技巧——文本撤销、恢复功能的实现
- Java应用技巧:对于 Cookie 的操作详解
- Java应用技巧-Java基础-Java-编程开发
- Java应用技巧:对于 Cookie 的操作详解