Java并发库(五、六、七):线程范围内共享数据、ThreadLocal、共享数据的三种方法

深切怀念传智播客张孝祥老师,特将其代表作——Java并发库视频研读两遍,受益颇丰,记以后阅

05. 线程范围内共享变量的概念与作用

线程范围内共享数据图解:


代码演示:

class ThreadScopeShareData

{

       三个模块共享数据,主线程模块和AB模块

       privatestatic int data = 0;      准备共享的数据

       存放各个线程对应的数据

       private Map<Thread, Integer>threadData = new HashMap<Thread, Integer>();

       publicstatic void main(String[] args)

       {     创建两个线程

for (int i=0;i<2; i++)

{

       new Thread(

new Runnable()

{

       public void run()

       {现在当前线程中修改一下数据,给出修改信息

              data = newRandom().nextInt();

              SOP(Thread.currentThread().getName()+将数据改为+data);

              将线程信息和对应数据存储起来

              threadData.put(Thread.currentThread(),data);

              使用两个不同的模块操作这个数据,看结果

              new A().get();

              new B().get();


}

}

).start();

}

}

       staticclass A

       {

       public void get()

       {

              data =threadData.get(Thread.currentThread());

       SOP(A+Thread.currentThread().getName()+拿到的数据+data);

}

}

       staticclass B

       {

       public void get()

       {

              data =threadData.get(Thread.currentThread());

       SOP(B+Thread.currentThread().getName()+拿到的数据+data);

}

}

}

结果并没与实现线程间的数据同步,两个线程使用的是同一个线程的数据。要解决这个问题,可以将每个线程用到的数据与对应的线程号存放到一个map集合中,使用数据时从这个集合中根据线程号获取对应线程的数据。代码实现:上面红色部分

程序中存在的问题:获取的数据与设置的数据不同步

               Thread-1共享数据设置为:-997057737

              Thread-1--A模块数据:-997057737

              Thread-0共享数据设置为:11858818

              Thread-0--A模块数据:11858818

              Thread-0--B模块数据:-997057737

              Thread-1--B模块数据:-997057737

最好将Runnable中设置数据的方法也写在对应的模块中,与获取数据模块互斥,以保证数据同步

public class ThreadScopeShareData {

 

       privatestatic int data = 0;

       privatestatic Map<Thread, Integer> threadData = new HashMap<Thread,Integer>();

       publicstatic void main(String[] args) {

              for(inti=0;i<2;i++){

                     newThread(new Runnable(){

                            @Override

                            publicvoid run() {

                                   intdata = new Random().nextInt();

                                   System.out.println(Thread.currentThread().getName()

                                                 +" has put data :" + data);

                                   threadData.put(Thread.currentThread(),data);

                                   newA().get();

                                   newB().get();

                            }

                     }).start();

              }

       }

      

       staticclass A{

              publicvoid get(){

                     intdata = threadData.get(Thread.currentThread());

                     System.out.println("Afrom " + Thread.currentThread().getName()

                                   +" get data :" + data);

              }

       }

      

       staticclass B{

              publicvoid get(){

                     intdata = threadData.get(Thread.currentThread());                   

                     System.out.println("Bfrom " + Thread.currentThread().getName()

                                   +" get data :" + data);

              }           

       }

}


public class ThreadScopeShareData {

	//private static int data = 0;
	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);
		}		
	}
}
一个最典型的例子就是JavaEE中的数据库操作的Connection。一个线程内的所有操作都用同一个Connection,如此才能统一进行Tx等操作。



 

06.ThreadLocal类及应用技巧

       多个模块在同一个线程中运行时要共享同一份数据,实现线程范围内的数据共享可以用上一节中所用的方法。

       JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于上一节中的Map。

ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码,多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。

       每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map集合中增加一条记录,key就是各自的线程,value就是各自的set方法传进去的值。

       在线程结束时可以调用ThreadLocal.clear()方法用来更快释放内存,也可以不调用,因为线程结束后也可以自动释放相关的ThreadLocal变量。

       一个ThreadLocal对象只能记录一个线程内部的一个共享变量,需要记录多个共享数据,可以创建多个ThreadLocal对象,或者将这些数据进行封装,将封装后的数据对象存入ThreadLocal对象中。

       将数据对象封装成单例,同时提供线程范围内的共享数据的设置和获取方法,提供已经封装好了的线程范围内的对象实例,使用时只需获取实例对象即可实现数据的线程范围内的共享,因为该对象已经是当前线程范围内的对象了。下边给出张老师的优雅代码:

package cn.itheima;

import java.util.Random;

publicclassThreadLocalShareDataDemo

{   /**06.ThreadLocal类及应用技巧

     * 将线程范围内共享数据进行封装,封装到一个单独的数据类中,提供设置获取方法

     * 将该类单例化,提供获取实例对象的方法,获取到的实例对象是已经封装好的当前线程范围内的对象

     */

    publicstaticvoidmain(String[] args)

    {

        for (inti=0; i<2; i++)

        {

            newThread(

                    newRunnable()

                    {                      

                        publicvoidrun()

                        {

                            intdata =new Random().nextInt(889);

    System.out.println(Thread.currentThread().getName()+"产生数据:"+data);

                            MyDatamyData = MyData.getInstance();

                            myData.setAge(data);

                            myData.setName("Name:"+data);

                            newA().get();

                            newB().get();

                        }

                    }).start();

        }

    }

   

    staticclass A

    {   //可以直接使用获取到的线程范围内的对象实例调用相应方法

        Stringname = MyData.getInstance().getName();

        intage =MyData.getInstance().getAge();

        publicvoidget()

        {

            System.out.println(Thread.currentThread().getName()+"--AA name:"+name+"...age:"+age);

        }

    }  

   

    staticclass B

    {

        //可以直接使用获取到的线程范围内的对象实例调用相应方法

        Stringname = MyData.getInstance().getName();

        intage =MyData.getInstance().getAge();

        publicvoidget()

        {

            System.out.println(Thread.currentThread().getName()+"--BB name:"+name+"...age:"+age);

        }

    }  

   

    staticclassMyData

    {

        privateStringname;

        privateintage;

        publicString getName()

        {

            returnname;

        }

        publicvoidsetName(String name)

        {

            this.name =name;

        }

        publicintgetAge()

        {

            returnage;

        }

        publicvoidsetAge(int age)

        {

            this.age =age;

        }

        //单例

        privateMyData() {};

        //提供获取实例方法

        publicstaticMyData getInstance()

        {

            //从当前线程范围内数据集中获取实例对象

            MyDatainstance = threadLocal.get();

            if(instance==null)

            {

                instance= new MyData();

                threadLocal.set(instance);

            }

            returninstance;

        }

        //将实例对象存入当前线程范围内数据集中

        staticThreadLocal<MyData>threadLocal =newThreadLocal<MyData>();

    }

}

 

 

 

 

张孝祥老师的代码

public class ThreadLocalTest {

 

    privatestatic ThreadLocal<Integer> x = new ThreadLocal<Integer>();

    privatestatic ThreadLocal<MyThreadScopeData> myThreadScopeData = newThreadLocal<MyThreadScopeData>();

    publicstatic void main(String[] args) {

        for(inti=0;i<2;i++){

            newThread(new Runnable(){

                @Override

                publicvoid run() {

                    intdata = new Random().nextInt();

                    System.out.println(Thread.currentThread().getName()

                            +" has put data :" + data);

                    x.set(data);

/*                  MyThreadScopeDatamyData = new MyThreadScopeData();

                    myData.setName("name"+ data);

                    myData.setAge(data);

                    myThreadScopeData.set(myData);*/

                    MyThreadScopeData.getThreadInstance().setName("name"+ data);

                    MyThreadScopeData.getThreadInstance().setAge(data);

                    newA().get();

                    newB().get();

                }

            }).start();

        }

    }

   

    staticclass A{

        publicvoid get(){

            intdata = x.get();

            System.out.println("A from " +Thread.currentThread().getName()

                    +" get data :" + data);

/*          MyThreadScopeDatamyData = myThreadScopeData.get();;

            System.out.println("Afrom " + Thread.currentThread().getName()

                    +" getMyData: " + myData.getName() + "," +

                    myData.getAge());*/

            MyThreadScopeDatamyData = MyThreadScopeData.getThreadInstance();

            System.out.println("Afrom " + Thread.currentThread().getName()

                    +" getMyData: " + myData.getName() + "," +

                    myData.getAge());

        }

    }

   

    staticclass B{

        publicvoid get(){

            intdata = x.get();        

            System.out.println("Bfrom " + Thread.currentThread().getName()

                    +" get data :" + data);

            MyThreadScopeDatamyData = MyThreadScopeData.getThreadInstance();

            System.out.println("Bfrom " + Thread.currentThread().getName()

                    +" getMyData: " + myData.getName() + "," +

                    myData.getAge());          

        }      

    }

}

 

class MyThreadScopeData{

    privateMyThreadScopeData(){}

    publicstatic /*synchronized*/ MyThreadScopeData getThreadInstance(){

        MyThreadScopeDatainstance = map.get();

        if(instance== null){

            instance= new MyThreadScopeData();

            map.set(instance);

        }

        returninstance;

    }

    //privatestatic MyThreadScopeData instance = null;//new MyThreadScopeData();

    privatestatic ThreadLocal<MyThreadScopeData> map = newThreadLocal<MyThreadScopeData>();

   

    privateString name;

    privateint age;

    publicString getName() {

        returnname;

    }

    publicvoid setName(String name) {

        this.name= name;

    }

    publicint getAge() {

        returnage;

    }

    publicvoid setAge(int age) {

        this.age= age;

    }

}

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);
					x.set(data);
/*					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(){
			int data = x.get();
			System.out.println("A from " + Thread.currentThread().getName() 
					+ " get data :" + data);
/*			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.getThreadInstance();
			System.out.println("B from " + Thread.currentThread().getName() 
					+ " getMyData: " + myData.getName() + "," +
					myData.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 instance = null;//new MyThreadScopeData();
	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;
	}
}

 

 

07. 多个线程之间共享数据的方式探讨

       例子:卖票:多个窗口同时卖这100张票,票就需要多个线程共享

a、如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个对象中有共享数据。

卖票就可以这样做,每个窗口都在做卖票任务,卖的票都是同一个数据。

public classMultiThreadShareData {   

       public static void main(String[] args) {

              ShareData1 data1 = new ShareData1();

              newThread(data1).start();

new Thread(data1).start();

       }

 

}

 

class ShareData1implements Runnable{

              private int count = 100;

              @Override

              public void run() {

                     // TODO Auto-generatedmethod stub

                     while(true){

                            count--;

                     }

              }

}

b、如果每个线程执行的代码不同,就需要使用不同的Runnable对象,有两种方式实现

Runnable对象之间的数据共享:

a)  将共享数据单独封装到一个对象中,同时在对象中提供操作这些共享数据的方法,可以方便实现对共享数据各项操作的互斥和通信。

public classMultiThreadShareData {    

public static void main(String[] args) {       

        final ShareData1 data1 = new ShareData1();

        new Thread(new Runnable(){

               @Override

               public void run() {

                      data1.decrement();

                     

               }

        }).start();

        new Thread(new Runnable(){

               @Override

               public void run() {

                      data1.increment();

                     

               }

        }).start();

 

}

 

}

 

class ShareData1{

        private int j = 0;

        public synchronized void increment(){

               j++;

        }

       

        public synchronized void decrement(){

               j--;

        }

}

b)  将各个Runnable对象作为某个类的内部类,共享数据作为外部类的成员变量,对共享数据的操作方法也在外部类中提供,以便实现互斥和通信,内部类的Runnable对象调用外部类中操作共享数据的方法即可。

public classMultiThreadShareData {    

public static void main(String[] args) {       

     ShareData1 data2 = newShareData1();

        newThread(new MyRunnable1(data2)).start();

        newThread(new MyRunnable2(data2)).start();

 

}

 

}

class MyRunnable1 implementsRunnable{

        private ShareData1 data1;

        public MyRunnable1(ShareData1 data1){

               this.data1= data1;

        }

        public void run() {

               data1.decrement();

              

        }

}

class MyRunnable2 implements Runnable{

        privateShareData1 data1;

        publicMyRunnable2(ShareData1 data1){

               this.data1= data1;

        }

        public void run() {

               data1.increment();

        }

}

class ShareData1{

        private int j = 0;

        public synchronized void increment(){

               j++;

        }

       

        public synchronized void decrement(){

               j--;

        }

}

 

       注意:要同步互斥的几段代码最好分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值