接着學習Java中的線程,線程范圍內的共享數據!
一、線程范圍內的數據共享定義
對於相同的程序代碼,多個模塊在同一個線程中共享一份數據,而在另外線程中運行時又共享另外一份數據。
共享數據中存在的問題,代碼如下:
1 // A 和 B共享數據data,但是在這種情況下 會存在問題 2 public class ThreadScopeShareData { 3 4 private static int data = 0; 5 6 public static void main(String[] args) { 7 for (int i = 0; i < 10; i++) { 8 new Thread(new Runnable() { 9 @Override 10 public void run() { 11 data = new Random().nextInt(); 12 System.out.println(Thread.currentThread().getName() + "has put data " + data); 13 new A().get(); 14 new B().get(); 15 } 16 }).start(); 17 } 18 } 19 20 static class A { 21 public void get() { 22 System.out.println("A from " + Thread.currentThread().getName() + "has put data " + data); 23 } 24 } 25 26 static class B { 27 public void get() { 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 class ThreadScopeShareData { 2 3 private static int data = 0; 4 // 這個用來存放當前線程內的共享數據 5 private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>(); 6 7 public static void main(String[] args) { 8 for (int i = 0; i < 10; i++) { 9 new Thread(new Runnable() { 10 @Override 11 public void run() { 12 int data = new Random().nextInt(); 13 System.out.println(Thread.currentThread().getName() + "has put data " + data); 14 threadData.put(Thread.currentThread(), data); 15 new A().get(); 16 new B().get(); 17 } 18 }).start(); 19 } 20 } 21 22 static class A { 23 public void get() { 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 class B { 30 public void get() { 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 class MyThreadScopeData { 3 4 private String name; 5 private int age; 6 //private static MyThreadScopeData instance = null; 7 private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); 8 9 private MyThreadScopeData() { 10 11 } 12 13 public static /* synchronized */ MyThreadScopeData getThreadInstance() { 14 MyThreadScopeData instance = map.get(); 15 if(instance == null) { 16 instance = new MyThreadScopeData(); 17 map.set(instance); 18 } 19 return instance; 20 } 21 22 public String getName() { 23 return name; 24 } 25 26 public void setName(String name) { 27 this.name = name; 28 } 29 30 public int getAge() { 31 return age; 32 } 33 34 public void setAge(int age) { 35 this.age = age; 36 } 37 38 }
測試類中代碼如下:
1 public class ThreadLocalTest { 2 3 private static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); 4 private static ThreadLocal<MyThreadScopeData> myThreadLocal = new ThreadLocal<MyThreadScopeData>(); 5 6 public static void main(String[] args) { 7 // 相當於創建了10個線程 8 for (int i = 0; i < 10; i++) { 9 new Thread(new Runnable() { 10 @Override 11 public void run() { 12 int data = new Random().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 new A().get(); 26 new B().get(); 27 } 28 }).start(); 29 } 30 } 31 32 static class A { 33 public void get() { 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 class B { 48 public void get() { 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 class MultiThreadShareData { 2 3 public static void main(String[] args) { 4 final ShareData1 data1 = new ShareData1(); 5 6 new Thread(new Runnable() { 7 @Override 8 public void run() { 9 data1.decrement(); 10 } 11 }).start(); 12 13 new Thread(new Runnable() { 14 @Override 15 public void run() { 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 void increment() { 27 j++; 28 } 29 30 public synchronized void decrement() { 31 j--; 32 } 33 34 /* 35 * @Override public void run() { while (true) { count--; } } 36 */ 37 } 38 39 }
