• 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事务)就要确保他们使用的是同一个数据库连接.那么怎样确保使用同一个数据库连接呢?





  • 相关阅读:
    1388:Lake Counting
    1253 Dungeon Master
    Ubuntu18.04下可以完美运行Quake3 Arena
    Windows下python3生成UTF8的CSV文件和sha256sum踩坑记录
    ROM后缀含义
    Ubuntu18.04下的模拟神器RetroArch
    廉价的SUP掌机拆解
    Python3连接MySQL
    Ubuntu18.04的网络管理netplan和防火墙ufw
    Ubuntu18.04命令行连接WiFi
  • 原文地址:https://www.cnblogs.com/gavanwanggw/p/6952958.html
Copyright © 2020-2023  润新知