线程高级应用-心得3-线程范围内的共享变量以及应用场景及面试题案例分析

1.知识点普及

2.案例说明:线程范围内的共享变量以及应用场景(转账,转入和转出);在线程内共享,在线程外独立

  1 package com.itcast.family;
  2 
  3 import java.util.HashMap;
  4 import java.util.Map;
  5 import java.util.Random;
  6 
  7 /**
  8  * 案例说明:线程范围内的共享变量以及应用场景(转账,转入和转出);在线程内共享,在线程外独立
  9  */
 10 public class ThreadScopeShareData {
 11 
 12     //使不同模块拿到相同线程的相同数据
 13     private static Map<Thread,Integer> threadData = new HashMap<Thread, Integer>();
 14     public static void main(String[] args) {
 15         for (int i = 0; i < 2; i++) {
 16             new Thread(new Runnable() {
 17 
 18                 @Override
 19                 public void run() {
 20                     int data = new Random().nextInt();
 21                     System.out.println(Thread.currentThread().getName()
 22                             + " has put data: " + data);
 23                     threadData.put(Thread.currentThread(), data);
 24                     new A().get();
 25                     new B().get();
 26                 }
 27             }).start();
 28         }
 29     }
 30 
 31     static class A {
 32         public void get() {
 33             int data = threadData.get(Thread.currentThread());
 34             System.out.println("A from " + Thread.currentThread().getName()
 35                     + " get data: " + data);
 36         }
 37     }
 38 
 39     static class B {
 40         public void get() {
 41             int data = threadData.get(Thread.currentThread());
 42             System.out.println("B from " + Thread.currentThread().getName()
 43                     + " get data: " + data);
 44         }
 45     }
 46 }
 47 3. 使用ThreadLocal达到与上个案例相同的需求
 48 package com.itcast.family;
 49 
 50 import java.util.Random;
 51 
 52 /**
 53  * 使用ThreadLocal达到与上个案例相同的需求
 54  */
 55 public class ThreadLocalTest {
 56 
 57 
 58     //使不同模块拿到相同线程的相同数据
 59     //直接使用ThreadLocal传入数据
 60     private static ThreadLocal<Integer> threadData = new ThreadLocal<Integer>();
 61     //使用ThreadLocal传入对象中封装的数据;普通思路用到的变量
 62     //private static ThreadLocal<MyThreadScopeData> myData = new ThreadLocal<MyThreadScopeData>();
 63     public static void main(String[] args) {
 64         for (int i = 0; i < 2; i++) {
 65             new Thread(new Runnable() {
 66 
 67                 @Override
 68                 public void run() {
 69                     int data = new Random().nextInt();
 70                     System.out.println(Thread.currentThread().getName()
 71                             + " has put data: " + data);
 72                     threadData.set(data);
 73                     
 74                     //一般思路,符合基本的面向对象编程,但是用着啰嗦臃肿,代码不够优化    
 75 /*                    MyThreadScopeData myThread = new MyThreadScopeData();
 76                     myThread.setName("get nameData: "+data);
 77                     myThread.setAge(data);
 78                     myData.set(myThread);*/
 79                     
 80                     
 81                     //优雅和优化的思路,类似单例模式封装代码,因为使用者只需知道怎么用干什么用的,而不需知道内部代码是什么
 82                     MyThreadScopeData.getThreadInstance().setName(" get nameData: "+data);
 83                     MyThreadScopeData.getThreadInstance().setAge(data);
 84                     new A().get();
 85                     new B().get();
 86                 }
 87             }).start();
 88         }
 89     }
 90 
 91     static class A {
 92         public void get() {
 93             int data = threadData.get();
 94             System.out.println("A from " + Thread.currentThread().getName()
 95                     + " get data: " + data);
 96     
 97             //一般思路,符合基本的面向对象编程,但是用着啰嗦臃肿,代码不够优化
 98 /*            MyThreadScopeData myThread = myData.get();
 99             System.out.println("A from " + Thread.currentThread().getName()
100                      + myThread.getName()+" ;get ageData: "+myThread.getAge());*/
101             
102             
103             //优雅和优化的思路,类似单例模式封装代码,因为使用者只需知道怎么用干什么用的,而不需知道内部代码是什么
104             MyThreadScopeData myThread = MyThreadScopeData.getThreadInstance();
105             System.out.println("A from " + Thread.currentThread().getName()
106                      + myThread.getName()+" ; get ageData: "+myThread.getAge());
107         }
108     }
109 
110     static class B {
111         public void get() {
112             int data = threadData.get();
113             System.out.println("B from " + Thread.currentThread().getName()
114                     + " get data: " + data);
115             
116 /*            MyThreadScopeData myThread = myData.get();
117             System.out.println("B from " + Thread.currentThread().getName()
118                      + myThread.getName()+" ;get ageData: "+myThread.getAge());*/
119             
120             MyThreadScopeData myThread = MyThreadScopeData.getThreadInstance();
121             System.out.println("B from " + Thread.currentThread().getName()
122                      + myThread.getName()+" ; get ageData: "+myThread.getAge());
123         }
124     }
125 }
126 
127 class MyThreadScopeData{
128     //可以参考单例模式来编写更加优化和优雅的线程代码
129     private MyThreadScopeData(){}
130      //线程这里与单例有些许不同,因为只有一个对象,所以可以不用使用同步锁
131     public static/*synchronized*/MyThreadScopeData getThreadInstance(){
132         MyThreadScopeData instance = map.get();
133         if(instance == null){
134             instance = new MyThreadScopeData();
135             map.set(instance);
136         }
137         return instance;
138     }
139     
140     /*只是告诉阅读者类似单例代码,但是与单例不同
141      * private static MyThreadScopeData intance = null;//new MyThreadScopeData(); 
142     //饱汉式,不管用不用先new出来;这里用饿汉式,用的时候才new出来
143     */    
144     private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
145     private String name;
146     private int age;
147     public String getName() {
148         return name;
149     }
150     public void setName(String name) {
151         this.name = name;
152     }
153     public int getAge() {
154         return age;
155     }
156     public void setAge(int age) {
157         this.age = age;
158     }
159     
160 }
161 4. 面试题: 设计4个成员,其中两个线程每次对j加1;另两个线程每次对j减1,代码如下分析:
162 
163 package com.itcast.family;
164 
165 /**
166  * 面试题:
167  *    设计4个成员,其中两个线程每次对j加1;
168  * 另两个线程每次对j减1,代码如下分析:
169  */
170 public class MultiThreadShareData {
171     
172     // 面试题答案:
173     private int j;
174 
175     public static void main(String[] args) {
176         MultiThreadShareData data = new MultiThreadShareData();
177         Inc inc = data.new Inc();
178         Dec dec = data.new Dec();
179         //创建四个线程,如题:两个增加两个减少
180         for (int i = 0; i < 2; i++) {
181             new Thread(inc).start();
182             new Thread(dec).start();
183         }
184     }
185 
186     /*
187      * 不直接把j++或j--写到对应的实现Runnable类的代码中,
188      * 而是抽出成外部类的两个方法;是因为外部类的方法方便使用
189      * 同步锁,更容易控制并发问题
190      */
191     private synchronized void inc() {
192         j++;
193         System.out.println(Thread.currentThread().getName() + "-inc:" + j);
194     }
195 
196     private synchronized void dec() {
197         j--;
198         System.out.println(Thread.currentThread().getName() + "-dec:" + j);
199     }
200 
201     class Inc implements Runnable {
202 
203         @Override
204         public void run() {
205             for (int i = 0; i < 100; i++) {
206                 inc();
207             }
208         }
209 
210     }
211 
212     class Dec implements Runnable {
213 
214         @Override
215         public void run() {
216             for (int i = 0; i < 100; i++) {
217                 dec();
218             }
219         }
220 
221     }
222     
223     
224 
225     
226     /*//第一种方案传入的data变量要么是静态成员变量要么是最终普通变量,否则会出错;因为两个内部类访问可以访问同一个外部类的成员变量
227     private static ShareData data1 = new ShareData();
228     public static void main(String[] args) {
229         final ShareData data1 = new ShareData();
230         ShareData data2 = new ShareData();
231         
232         //第一种方案:这两个线程分别实现两个Runnable的run方法,分别用来执行加减
233         new Thread(new Runnable() {
234             
235             @Override
236             public void run() {
237                 data1.increment();
238             }
239         }).start();
240         new Thread(new Runnable() {
241             
242             @Override
243             public void run() {
244                 data1.decrement();
245             }
246         }).start();
247         
248         //第二种方案:传入实现了Runnable接口的类对象,相应的需求方法封装在传入的类中
249         new Thread(new MyRunnable1(data2)).start();
250         new Thread(new MyRunnable2(data2)).start();
251 
252     }
253 
254 }
255 
256 //第二种方案
257 class MyRunnable1 implements Runnable{
258     private ShareData data;
259     MyRunnable1(ShareData data) {
260         super();
261         this.data = data;
262     }
263 
264     public void run() {
265         data.increment();
266     }
267 }
268 class MyRunnable2 implements Runnable{
269     private ShareData data;
270     MyRunnable2(ShareData data) {
271         super();
272         this.data = data;
273     }
274     
275     public void run() {
276         data.decrement();
277     }
278 }
279 
280 
281 //因为需求是一个加一个减,即两个不同的线程,仅实现一个Runnable接口是不够的
282 class ShareData{ implements Runnable{
283     private int count = 100;
284     @Override
285     public void run() {
286         while(true){
287             count --;
288         }
289     }
290    
291     private int j = 0;
292     public void increment(){
293         j++;
294     }
295     public void decrement(){
296         j--;
297 
298     }
299     */
300 }

 

posted @ 2017-01-08 13:38  chenxiangxiang  阅读(367)  评论(0编辑  收藏  举报