线程高级应用-心得3-线程范围内的共享变量以及应用场景及面试题案例分析
1.知识点普及
2.案例说明:线程范围内的共享变量以及应用场景(转账,转入和转出);在线程内共享,在线程外独立
package com.itcast.family; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * 案例说明:线程范围内的共享变量以及应用场景(转账,转入和转出);在线程内共享,在线程外独立 */ public class ThreadScopeShareData { //使不同模块拿到相同线程的相同数据 private static Map<Thread,Integer> threadData = new HashMap<Thread, Integer>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data: " + data); threadData.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); } } static class A { public void get() { int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " get data: " + data); } } static class B { public void get() { int data = threadData.get(Thread.currentThread()); System.out.println("B from " + Thread.currentThread().getName() + " get data: " + data); } } } 3. 使用ThreadLocal达到与上个案例相同的需求 package com.itcast.family; import java.util.Random; /** * 使用ThreadLocal达到与上个案例相同的需求 */ public class ThreadLocalTest { //使不同模块拿到相同线程的相同数据 //直接使用ThreadLocal传入数据 private static ThreadLocal<Integer> threadData = new ThreadLocal<Integer>(); //使用ThreadLocal传入对象中封装的数据;普通思路用到的变量 //private static ThreadLocal<MyThreadScopeData> myData = new ThreadLocal<MyThreadScopeData>(); public static void main(String[] args) { for (int i = 0; i < 2; i++) { new Thread(new Runnable() { @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data: " + data); threadData.set(data); //一般思路,符合基本的面向对象编程,但是用着啰嗦臃肿,代码不够优化 /* MyThreadScopeData myThread = new MyThreadScopeData(); myThread.setName("get nameData: "+data); myThread.setAge(data); myData.set(myThread);*/ //优雅和优化的思路,类似单例模式封装代码,因为使用者只需知道怎么用干什么用的,而不需知道内部代码是什么 MyThreadScopeData.getThreadInstance().setName(" get nameData: "+data); MyThreadScopeData.getThreadInstance().setAge(data); new A().get(); new B().get(); } }).start(); } } static class A { public void get() { int data = threadData.get(); System.out.println("A from " + Thread.currentThread().getName() + " get data: " + data); //一般思路,符合基本的面向对象编程,但是用着啰嗦臃肿,代码不够优化 /* MyThreadScopeData myThread = myData.get(); System.out.println("A from " + Thread.currentThread().getName() + myThread.getName()+" ;get ageData: "+myThread.getAge());*/ //优雅和优化的思路,类似单例模式封装代码,因为使用者只需知道怎么用干什么用的,而不需知道内部代码是什么 MyThreadScopeData myThread = MyThreadScopeData.getThreadInstance(); System.out.println("A from " + Thread.currentThread().getName() + myThread.getName()+" ; get ageData: "+myThread.getAge()); } } static class B { public void get() { int data = threadData.get(); System.out.println("B from " + Thread.currentThread().getName() + " get data: " + data); /* MyThreadScopeData myThread = myData.get(); System.out.println("B from " + Thread.currentThread().getName() + myThread.getName()+" ;get ageData: "+myThread.getAge());*/ MyThreadScopeData myThread = MyThreadScopeData.getThreadInstance(); System.out.println("B from " + Thread.currentThread().getName() + myThread.getName()+" ; get ageData: "+myThread.getAge()); } } } class MyThreadScopeData{ //可以参考单例模式来编写更加优化和优雅的线程代码 private MyThreadScopeData(){} //线程这里与单例有些许不同,因为只有一个对象,所以可以不用使用同步锁 public static/*synchronized*/MyThreadScopeData getThreadInstance(){ MyThreadScopeData instance = map.get(); if(instance == null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; } /*只是告诉阅读者类似单例代码,但是与单例不同 * private static MyThreadScopeData intance = null;//new MyThreadScopeData(); //饱汉式,不管用不用先new出来;这里用饿汉式,用的时候才new出来 */ private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } 4. 面试题: 设计4个成员,其中两个线程每次对j加1;另两个线程每次对j减1,代码如下分析: package com.itcast.family; /** * 面试题: * 设计4个成员,其中两个线程每次对j加1; * 另两个线程每次对j减1,代码如下分析: */ public class MultiThreadShareData { // 面试题答案: private int j; public static void main(String[] args) { MultiThreadShareData data = new MultiThreadShareData(); Inc inc = data.new Inc(); Dec dec = data.new Dec(); //创建四个线程,如题:两个增加两个减少 for (int i = 0; i < 2; i++) { new Thread(inc).start(); new Thread(dec).start(); } } /* * 不直接把j++或j--写到对应的实现Runnable类的代码中, * 而是抽出成外部类的两个方法;是因为外部类的方法方便使用 * 同步锁,更容易控制并发问题 */ private synchronized void inc() { j++; System.out.println(Thread.currentThread().getName() + "-inc:" + j); } private synchronized void dec() { j--; System.out.println(Thread.currentThread().getName() + "-dec:" + j); } class Inc implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { inc(); } } } class Dec implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { dec(); } } } /*//第一种方案传入的data变量要么是静态成员变量要么是最终普通变量,否则会出错;因为两个内部类访问可以访问同一个外部类的成员变量 private static ShareData data1 = new ShareData(); public static void main(String[] args) { final ShareData data1 = new ShareData(); ShareData data2 = new ShareData(); //第一种方案:这两个线程分别实现两个Runnable的run方法,分别用来执行加减 new Thread(new Runnable() { @Override public void run() { data1.increment(); } }).start(); new Thread(new Runnable() { @Override public void run() { data1.decrement(); } }).start(); //第二种方案:传入实现了Runnable接口的类对象,相应的需求方法封装在传入的类中 new Thread(new MyRunnable1(data2)).start(); new Thread(new MyRunnable2(data2)).start(); } } //第二种方案 class MyRunnable1 implements Runnable{ private ShareData data; MyRunnable1(ShareData data) { super(); this.data = data; } public void run() { data.increment(); } } class MyRunnable2 implements Runnable{ private ShareData data; MyRunnable2(ShareData data) { super(); this.data = data; } public void run() { data.decrement(); } } //因为需求是一个加一个减,即两个不同的线程,仅实现一个Runnable接口是不够的 class ShareData{ implements Runnable{ private int count = 100; @Override public void run() { while(true){ count --; } } private int j = 0; public void increment(){ j++; } public void decrement(){ j--; } */ }