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

	}
	*/
}

posted @ 2013-02-07 14:06  yangkai_keven  阅读(264)  评论(0编辑  收藏  举报