接着学习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 }