线程高级应用-心得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 }