线程共享数据以及ThreadLocal类

来源:互联网 发布:sysgettempdir php 编辑:程序博客网 时间:2024/05/08 17:08
线程范围内共享变量的概念与作用


代码演示:
class ThreadScopeShareData
{
 三个模块共享数据,主线程模块和AB模块
 private static int data = 0; 准备共享的数据
 存放各个线程对应的数据
 private 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()
{
 public void run()
 {现在当前线程中修改一下数据,给出修改信息
  data = new Random().nextInt();
  SOP(Thread.currentThread().getName()+将数据改为+data);
  将线程信息和对应数据存储起来
  threadData.put(Thread.currentThread(), data);
  使用两个不同的模块操作这个数据,看结果
  new A().get();
  new B().get();
}
}
).start();
}
}
 static class A
 {
 public void get()
 {
  data = threadData.get(Thread.currentThread());
 SOP(A+Thread.currentThread().getName()+拿到的数据+data);
}
}
 static class 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中设置数据的方法也写在对应的模块中,与获取数据模块互斥,以保证数据同步




. ThreadLocal类及应用技巧
 多个模块在同一个线程中运行时要共享同一份数据,实现线程范围内的数据共享可以用上一节中所用的方法。
 JDK1.5提供了ThreadLocal类来方便实现线程范围内的数据共享,它的作用就相当于上一节中的Map。
 每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map集合中增加一条记录,key就是各自的线程,value就是各自的set方法传进去的值。
 在线程结束时可以调用ThreadLocal.clear()方法用来更快释放内存,也可以不调用,因为线程结束后也可以自动释放相关的ThreadLocal变量。
 一个ThreadLocal对象只能记录一个线程内部的一个共享变量,需要记录多个共享数据,可以创建多个ThreadLocal对象,或者将这些数据进行封装,将封装后的数据对象存入ThreadLocal对象中。
 将数据对象封装成单例,同时提供线程范围内的共享数据的设置和获取方法,提供已经封装好了的线程范围内的对象实例,使用时只需获取实例对象即可实现数据的线程范围内的共享,因为该对象已经是当前线程范围内的对象了。下边给出张老师的优雅代码:
package cn.itheima;
import java.util.Random;
public class ThreadLocalShareDataDemo
{ /**06. ThreadLocal类及应用技巧
  * 将线程范围内共享数据进行封装,封装到一个单独的数据类中,提供设置获取方法
  * 将该类单例化,提供获取实例对象的方法,获取到的实例对象是已经封装好的当前线程范围内的对象
  */
 public static void main(String[] args)
 {
  for (int i=0; i<2; i++)
  {
   new Thread(
     new Runnable()
     {      
      public void run()
      {
       int data = new Random().nextInt(889);
 System.out.println(Thread.currentThread().getName()+"产生数据:"+data);
       MyData myData = MyData.getInstance();
       myData.setAge(data);
       myData.setName("Name:"+data);
       new A().get();
       new B().get();
      }
     }).start();
  }
 }
 static class A
 { //可以直接使用获取到的线程范围内的对象实例调用相应方法
  String name = MyData.getInstance().getName();
  int age = MyData.getInstance().getAge();
  public void get() 
  {
   System.out.println(Thread.currentThread().getName()+"-- AA name:"+name+"...age:"+age);
  }
 } 
 static class B
 {
  //可以直接使用获取到的线程范围内的对象实例调用相应方法
  String name = MyData.getInstance().getName();
  int age = MyData.getInstance().getAge();
  public void get() 
  {
   System.out.println(Thread.currentThread().getName()+"-- BB name:"+name+"...age:"+age);
  }
 } 
 static class MyData
 {
  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;
  }
  //单例
  private MyData() {};
  //提供获取实例方法
  public static MyData getInstance()
  {
   //从当前线程范围内数据集中获取实例对象
   MyData instance = threadLocal.get();
   if (instance==null)
   {
    instance = new MyData();
    threadLocal.set(instance);
   }
   return instance;
  }
  //将实例对象存入当前线程范围内数据集中
  static ThreadLocal<MyData> threadLocal = new ThreadLocal<MyData>();
 }
}
0 0