Thread学习(八) ThreadLocal实现线程范围内的共享变量

什么叫线程范围内的共享数据(线程外独立)?

一个线程在运行的时候可以调用A模块,B模块,C模块,就比如我们调用A,B,C三个对象,A,B,C三个模块内部的代码就要访问外面的变量,那么此时如果我们把ABC调用的那个变量设置成static的静态变量,我们也可以实现让ABC同时访问同一个变量,但是如果我们用第二个线程去同时调用ABC去访问这个变量,这时候第二个线程访问到的这个变量就不是刚才我们线程1所访问的变量了!!!而是另外一个数据,也就是说static的变量在被同一个线程访问的时候这个变量是相同的,当我们换了线程访问,那么这个数据就不同了。那如果我们是5个线程访问这个变量,那么我们就会得到5个不同的变量。
public class ThreadScopShareData {
	private static int data = 0;//我们定义一个静态变量data
	
	static class A{//定义一个静态内部类
		public void getData(){
			System.out.println(Thread.currentThread().getName()+"从A中得到数据"+data);
		}
	}
	
	static class B{//定义第二个静态内部类
		public void getData(){
			System.out.println(Thread.currentThread().getName()+"从B中得到数据"+data);
		}
	}
	
	public static void main(String[] args) {
		//在主函数中创建两个线程
		for(int i=1;i<3;i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					data =new Random().nextInt();
					System.out.println(Thread.currentThread().getName()+"已经设置了数据"+data);
					new A().getData();
					new B().getData();
				}
			}).start();
			
		}
	}
}
我们运行上面的程序会发现结果如下
Thread-0已经设置了数据9296023
Thread-1已经设置了数据979192099
Thread-0从A中得到数据979192099
Thread-1从A中得到数据979192099
Thread-0从B中得到数据979192099
Thread-1从B中得到数据979192099
线程0设置的数据和线程0取得的数据是不同的,所以这里的数据就没有实现线程内的共享!

ThreadLocal就相当于一个map


现在我们把上面的程序改写一下
public class ThreadScopShareData {
	private static int data = 0;//我们定义一个静态变量data
	private static Map<Thread, Integer> threadData = new HashMap<Thread,Integer>();
	static class A{//定义一个静态内部类
		public void getData(){
			int data = threadData.get(Thread.currentThread());
			System.out.println(Thread.currentThread().getName()+"从A中得到数据"+data);
		}
	}
	
	static class B{//定义第二个静态内部类
		public void getData(){
			int data = threadData.get(Thread.currentThread());
			System.out.println(Thread.currentThread().getName()+"从B中得到数据"+data);
		}
	}
	
	public static void main(String[] args) {
		//在主函数中创建两个线程
		for(int i=1;i<3;i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					int data =new Random().nextInt();
					threadData.put(Thread.currentThread(), data);
					System.out.println(Thread.currentThread().getName()+"已经设置了数据"+data);
					new A().getData();
					new B().getData();
				}
			}).start();
			
		}
	}
}

我们创建一个map,然后为每个线程创建一个局部变量,然后以线程的名字单做key保存在线程中,这样我们会获得下面的结果
Thread-1已经设置了数据-1254485186
Thread-0已经设置了数据1394718209
Thread-1从A中得到数据-1254485186
Thread-0从A中得到数据1394718209
Thread-1从B中得到数据-1254485186
Thread-0从B中得到数据1394718209
我们发现此时线程1和线程2的这个变量相互独立了。

ThreadLocal

首先ThreadLocal和本地线程没有一毛钱关系,更不是一个特殊的Thread,它只是一个线程的局部变量(其实就是一个Map),ThreadLocal会为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。这样做其实就是以空间换时间的方式(与synchronized相反),以耗费内存为代价,单大大减少了线程同步(如synchronized)所带来性能消耗以及减少了线程并发控制的复杂度。
我们把上面的程序改成ThreadLocal实现
public class ThreadScopShareData {
	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
	public static void main(String[] args) {
		//在主函数中创建两个线程
		for(int i=1;i<3;i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					int data =new Random().nextInt();
					threadLocal.set(data);
					System.out.println(Thread.currentThread().getName()+"已经设置了数据"+data);
					new A().getData();
					new B().getData();
				}
			}).start();
			
		}
	}
	static class A{//定义一个静态内部类
		public void getData(){
			System.out.println(Thread.currentThread().getName()+"从A中得到数据"+threadLocal.get());
		}
	}
	
	static class B{//定义第二个静态内部类
		public void getData(){
			threadLocal.get();
			System.out.println(Thread.currentThread().getName()+"从B中得到数据"+threadLocal.get());
		}
	}
	
}
运行结果
Thread-0已经设置了数据-732021060
Thread-1已经设置了数据712253571
Thread-1从A中得到数据712253571
Thread-0从A中得到数据-732021060
Thread-1从B中得到数据712253571
Thread-0从B中得到数据-732021060

多个变量怎么用ThreadLocal存储?

一个ThreadLocal就代表一个变量,那么要是想在一个线程中存储多个变量的时候怎么存储呢 ?
此时我们可以把多个变量封装成一个对象,然后把这个对象存储在ThreadLocal中就可以实现我们的需求了。。

我们把上面的代码改写一下,让我们的ThreadLocal可以存放多个变量,并且每个线程的ThreadLocal都是独立的。

/**
 * 
 * 
 * ThreadScopShareData
 * 创建人:LBM
 * 时间:2016年11月24日-下午3:03:03 
 * @version 1.0.0
 *
 */
public class ThreadScopShareData {
	public static void main(String[] args) {
		A a = new A();
		B b = new B();
		// 在主函数中创建两个线程
		for (int i = 1; i < 3; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					int data = new Random().nextInt();
					LocalData.getInstance().setAge(data);//创建自己的对象存放变量
					LocalData.getInstance().setName("name"+data);//创建自己的对象存放变量
					System.out.println(Thread.currentThread().getName() +data);
					a.getData();
					b.getData();
				}
			}).start();

		}
	}

	static class A {// 定义一个静态内部类
		public void getData() {
			LocalData localData =LocalData.getInstance();//创建自己线程内部的对象去获得变量
			System.out.println(Thread.currentThread().getName() + "从A中得到数据" + "age===" + localData.getAge()
					+ "name===" + localData.getName());
		}
	}
	static class B {// 定义第二个静态内部类
		public void getData() {
			LocalData localData =LocalData.getInstance();//创建自己线程内部的对象去获得变量
			System.out.println(Thread.currentThread().getName() + "从B中得到数据" + "age===" + localData.getAge()
					+ "name===" + localData.getName());
		}
	}

}
/**
 * 
 * 我们从线程的任意地方调用我的这个对象,都会得到和该线程相关的一个实例对象。
 * LocalData
 * 创建人:LBM
 * 时间:2016年11月24日-下午3:03:21 
 * @version 1.0.0
 *
 */
class LocalData {//创建一个对象
	private LocalData() {// 构造方法私有化

	}
	public static LocalData getInstance() {
		LocalData localData = threadLocal.get();//当我们从当前线程中的ThreadLocal中拿不到对象的时候我们就创建新的对象
		if (localData == null) {
			localData = new LocalData();
			threadLocal.set(localData);
		}
		return localData;
	}
	//在对象内部创建一个ThreadLocal,这样当每个线程来创建对象的时候,创建的都是这个线程自己内部的对象
	private static ThreadLocal<LocalData> threadLocal = new ThreadLocal<LocalData>();
	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;
	}

}


我们上面封装的对象把ThreadLocal封装在这个对象内部,然后通过单例模式的思想就实现了无论在线程的哪里调用这个对象,所得到的对象都是和这个线程相关的对象!每个线程调用getInstance创建对象的时候,都会进行判断,该线程是否存在这个对象,如果存在直接返回,如果不存在,创建一个新的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值