JAVA 并发编程-线程范围内共享变量(五)


线程范围内共享变量要实现的效果为:

 

多个对象间共享同一线程内的变量



未实现线程共享变量的demo


package cn.itcast.heima2;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class ThreadScopeShareData {

	private static int data = 0;
//	private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>();
	
	public static void main(String[] args) {
		//共启动2个线程
		for(int i=0;i<2;i++){
			//启动一个线程
			new Thread(new Runnable(){
				@Override
				public void run() {
					data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() 
							+ " has put data :" + data);
					//以当前线程为key值放入到map中。当取值时依据各自的线程取各自的数据
//					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);
		}		
	}
}

执行结果:




通过打印出的结果能够看出,当Thread-0获取了一个随机数,改动了data的值,正在睡眠的时候,Thread-1又获取了一个随机数,相同改动了data的值。然后Thread-1调用了静态内部类ABget方法。实际上此时的data已经是Thread-1拿到的随机数了。

当然,我们能够通过添加synchronized加锁来控制线程的执行。让Thread-0执行完方法之前,Thread-1不能改动data的值。

此外,还能够使用另外几种方法来获取线程执行时变量赋予的真正值。


线程范围内共享变量实现方式: 

 

Map实现方式:


package cn.itcast.heima2;

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) {
		//共启动2个线程
		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);
					//以当前线程为key值放入到map中,当取值时依据各自的线程取各自的数据
					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);
		}		
	}
}

执行结果:


 


ThreadLocal方式:


package cn.itcast.heima2;

import java.util.Random;

public class ThreadLocalTest {

	private static ThreadLocal<Integer> x = new ThreadLocal<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);
					x.set(data);
					new A().get();
					new B().get();
				}							
			}).start();
		}
	}
	
	static class A{
		public void get(){
			int data = x.get();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);
		}
	}
	
	static class B{
		public void get(){
			int data = x.get();			
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " get data :" + data);					
		}		
	}
}


存在的问题:一个ThreadLocal代表一个变量,故当中仅仅能放一个数据,假设你有两个变量要线程范围内共享,则要定义两个ThreadLocal。例如以下为解决方式:

 

扩展方式-单例方式处理对象:


package cn.itcast.heima2;

import java.util.Random;

public class ThreadLocalTest {

//	方式一
//	private static ThreadLocal<Integer> x = new ThreadLocal<Integer>();
	
	private static ThreadLocal<MyThreadScopeData> myThreadScopeData = 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);
//					方式一 ThreadLocal
//					x.set(data);
//					方式二 new对象方式,将多个属性放到对象中
//					MyThreadScopeData myData = new MyThreadScopeData();
//					myData.setName("name" + data);
//					myData.setAge(data);
//					myThreadScopeData.set(myData);
//					方式三 使用单例模式
					MyThreadScopeData.getThreadInstance().setName("name" + data);
					MyThreadScopeData.getThreadInstance().setAge(data);
					
					new A().get();
					new B().get();
				}							
			}).start();
		}
	}
	
	static class A{
		public void get(){
//			方式一 ThreadLocal
//			int data = x.get();
//			System.out.println("A from " + Thread.currentThread().getName() 
//					+ " get data :" + data);
//			方式二 new对象方式,将多个属性放到对象中
//			MyThreadScopeData myData = myThreadScopeData.get();;
//			System.out.println("A from " + Thread.currentThread().getName() 
//					+ " getMyData: " + myData.getName() + "," +
//					myData.getAge());
//			方式三 使用单例模式
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.getAge());
		}
	}
	
	static class B{
		public void get(){
//			int data = x.get();			
//			System.out.println("B from " + Thread.currentThread().getName() 
//					+ " get data :" + data);
//			MyThreadScopeData myData = myThreadScopeData.get();;
//			System.out.println("B from " + Thread.currentThread().getName() 
//					+ " getMyData: " + myData.getName() + "," +
//					myData.getAge());
			MyThreadScopeData myData = MyThreadScopeData.getThreadInstance();
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.getAge());			
		}		
	}
}

class MyThreadScopeData{
	
	private MyThreadScopeData(){}
	
	private static MyThreadScopeData instance = null;//new MyThreadScopeData();
	
	private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
	
	public static /*synchronized*/ MyThreadScopeData getThreadInstance(){
		MyThreadScopeData instance = map.get();
		if(instance == null){
			instance = new MyThreadScopeData();
			map.set(instance);
		}
		return instance;
	}
	
	
	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;
	}
}


总结:

 

    synchronized和使用ThreadLocal均能够解决以上的问题。仅仅是这是两种不同的方式。synchronized是依赖锁的机制一个执行完后还有一个再执行。ThreadLocal会为每个线程维护一个和该线程绑定的变量的副本,从而隔离了多个线程的数据,每个线程都拥有自己的变量副本。从而也就没有必要对该变量进行同步了。

    概括起来说,对于多线程资源共享的问题,同步机制採用了“以时间换空间”的方式,而ThreadLocal採用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队訪问,而后者为每个线程都提供了一份变量,因此能够同一时候訪问而互不影响。

    当然ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发訪问。是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不须要对多个线程进行同步了。

    ThreadLocal的应用:

    在业务逻辑层须要调用多个Dao层的方法。我们要保证事务(jdbc事务)就要确保他们使用的是同一个数据库连接.那么怎样确保使用同一个数据库连接呢?





原文地址:https://www.cnblogs.com/gavanwanggw/p/6952958.html