接着学习Java中的线程,线程范围内的共享数据!
一、线程范围内的数据共享定义
对于相同的程序代码,多个模块在同一个线程中共享一份数据,而在另外线程中运行时又共享另外一份数据。
共享数据中存在的问题,代码如下:
1 //A 和 B共享数据data,但是在这种情况下 会存在问题
2 public classThreadScopeShareData {3
4 private static int data = 0;5
6 public static voidmain(String[] args) {7 for (int i = 0; i < 10; i++) {8 new Thread(newRunnable() {9 @Override10 public voidrun() {11 data = newRandom().nextInt();12 System.out.println(Thread.currentThread().getName() + "has put data " +data);13 newA().get();14 newB().get();15 }16 }).start();17 }18 }19
20 static classA {21 public voidget() {22 System.out.println("A from " + Thread.currentThread().getName() + "has put data " +data);23 }24 }25
26 static classB {27 public voidget() {28 System.out.println("B from " + Thread.currentThread().getName() + "has put data " +data);29 }30 }31
32 }
运行结果如下:(好像是有点乱七八糟的感觉)
1 Thread-3has put data 1233171571
2 Thread-7has put data -1796246182
3 Thread-1has put data -609826403
4 A from Thread-3has put data 1961867182
5 A from Thread-1has put data 1961867182
6 Thread-8has put data 2116621494
7 A from Thread-8has put data 1961867182
8 Thread-5has put data -609826403
9 A from Thread-5has put data 1961867182
10 A from Thread-7has put data 1961867182
11 B from Thread-7has put data 1961867182
12 B from Thread-5has put data 1961867182
13 Thread-6has put data -609826403
14 A from Thread-6has put data 1961867182
15 B from Thread-6has put data 1961867182
16 Thread-0has put data 1233171571
17 A from Thread-0has put data 1961867182
18 B from Thread-0has put data 1961867182
19 Thread-9has put data 1961867182
20 A from Thread-9has put data 1961867182
21 B from Thread-9has put data 1961867182
22 B from Thread-1has put data 1961867182
23 Thread-2has put data 1233171571
24 Thread-4has put data 1233171571
25 A from Thread-4has put data 1961867182
26 B from Thread-4has put data 1961867182
27 B from Thread-8has put data 1961867182
28 B from Thread-3has put data 1961867182
29 A from Thread-2has put data 1961867182
30 B from Thread-2has put data 1961867182
解决方案如下,用线程范围内的变量,当然这个是比较粗糙的解决方案,代码如下:
1 public classThreadScopeShareData {2
3 private static int data = 0;4 //这个用来存放当前线程内的共享数据
5 private static Map threadData = new HashMap();6
7 public static voidmain(String[] args) {8 for (int i = 0; i < 10; i++) {9 new Thread(newRunnable() {10 @Override11 public voidrun() {12 int data = newRandom().nextInt();13 System.out.println(Thread.currentThread().getName() + "has put data " +data);14 threadData.put(Thread.currentThread(), data);15 newA().get();16 newB().get();17 }18 }).start();19 }20 }21
22 static classA {23 public voidget() {24 int data =threadData.get(Thread.currentThread());25 System.out.println("A from " + Thread.currentThread().getName() + "has put data " +data);26 }27 }28
29 static classB {30 public voidget() {31 int data =threadData.get(Thread.currentThread());32 System.out.println("B from " + Thread.currentThread().getName() + "has put data " +data);33 }34 }35
36 }
二、JDK中解决线程共享数据(ThreadLocal)
优化解决方法,更加优雅的代码,更加人性化的解决方法,使得用户用起来更加方便,封装到ThreadLocal中,并且得保证同一个线程,所得到的的是同一份数据!
改造之后的实体对象,代码如下:
1 //改造之后的实体类,封装创建方法,并且封装ThreadLocal,来保证同一个线程得到的同一个对象
2 public classMyThreadScopeData {3
4 privateString name;5 private intage;6 //private static MyThreadScopeData instance = null;
7 private static ThreadLocal map = new ThreadLocal();8
9 privateMyThreadScopeData() {10
11 }12
13 public static /*synchronized*/MyThreadScopeData getThreadInstance() {14 MyThreadScopeData instance =map.get();15 if(instance == null) {16 instance = newMyThreadScopeData();17 map.set(instance);18 }19 returninstance;20 }21
22 publicString getName() {23 returnname;24 }25
26 public voidsetName(String name) {27 this.name =name;28 }29
30 public intgetAge() {31 returnage;32 }33
34 public void setAge(intage) {35 this.age =age;36 }37
38 }
测试类中代码如下:
1 public classThreadLocalTest {2
3 private static ThreadLocal x = new ThreadLocal();4 private static ThreadLocal myThreadLocal = new ThreadLocal();5
6 public static voidmain(String[] args) {7 //相当于创建了10个线程
8 for (int i = 0; i < 10; i++) {9 new Thread(newRunnable() {10 @Override11 public voidrun() {12 int data = newRandom().nextInt();13 System.out.println(Thread.currentThread().getName() + "has put data " +data);14 //MyThreadScopeData myData = new MyThreadScopeData();15 //myData.setName("name" + data);16 //myData.setAge(data);17 //myThreadLocal.set(myData);
18
19 MyThreadScopeData.getThreadInstance().setName("name" +data);20 MyThreadScopeData.getThreadInstance().setAge(data);21 //存放的是与当前线程相关的数据
22 x.set(data);23
24
25 newA().get();26 newB().get();27 }28 }).start();29 }30 }31
32 static classA {33 public voidget() {34 int data =x.get();35 System.out.println("A from " + Thread.currentThread().getName() + "has get data " +data);36
37 //MyThreadScopeData myData = myThreadLocal.get();38 //System.out.println("A from " + Thread.currentThread().getName() + "has getMyData " + myData.getName() + ","39 //+ myData.getAge());
40
41 MyThreadScopeData myData =MyThreadScopeData.getThreadInstance();42 System.out.println("A from " + Thread.currentThread().getName() + "has getMyData " + myData.getName() + ","
43 +myData.getAge());44 }45 }46
47 static classB {48 public voidget() {49 int data =x.get();50 System.out.println("B from " + Thread.currentThread().getName() + "has get data " +data);51
52 //MyThreadScopeData myData = myThreadLocal.get();53 //System.out.println("B from " + Thread.currentThread().getName() + "has getMyData " + myData.getName() + ","54 //+ myData.getAge());
55
56 MyThreadScopeData myData =MyThreadScopeData.getThreadInstance();57 System.out.println("B from " + Thread.currentThread().getName() + "has getMyData " + myData.getName() + ","
58 +myData.getAge());59 }60 }61 }
总结:这个线程范围内的数据共享问题,解决的方法中用到了单例模式中的设计思想,但是区别的地方是加了一步将数据存放到ThreadLocal 中,实现数据的共享!
三、多线程访问共享数据和线程的方式
也是线程间数据共享的问题,只不过这个是以实战的角度来探索线程共享间数据的同步问题,主要学的是这种解决实际问题的能力,看看代码:
1 public classMultiThreadShareData {2
3 public static voidmain(String[] args) {4 final ShareData1 data1 = newShareData1();5
6 new Thread(newRunnable() {7 @Override8 public voidrun() {9 data1.decrement();10 }11 }).start();12
13 new Thread(newRunnable() {14 @Override15 public voidrun() {16 data1.increment();17 }18 }).start();19 }20
21 static class ShareData1 /*implements Runnable*/{22
23 private int j = 0;24 private int count = 100;25
26 public synchronized voidincrement() {27 j++;28 }29
30 public synchronized voiddecrement() {31 j--;32 }33
34 /*
35 * @Override public void run() { while (true) { count--; } }36 */
37 }38
39 }