ThreadLocal

来源:互联网 发布:关于农副产品的软件 编辑:程序博客网 时间:2024/04/27 13:06

ThredLocal不是一个线程,而是一个线程的本地化对象,当工作于多线程的对象用ThreadLocal维护变量(即共享资源)时,ThreadLocal会为每个使用该变量(即访问共享资源)的线程创建一个变量副本,每个线程操作的是ThreadLocal提供的变量副本,不会影响到其他线程,从而解决了共享资源在并发访问时带来的线程安全问题;
这个变量副本从线程的角度来说,就像是线程的一个本地变量或叫局部变量;

ThreadLocal的接口方法:
1 public void set(Object value);
设置当前线程的线程局部变量的值
2 public Object get();
返回当前线程的线程局部变量的值
3 public void remove();
将当前线程的线程局部变量的值删除,目的是较少内存占用
4 protected Object initialValue();
获取当前线程的线程局部变量的初始值,该方法是一个延迟调用方法,只有线程第一次调用set()或get()方法时,该方法才会被调用,且仅执行一次;
ThreadLocal如何做到为每个线程都维护一份独立的变量副本呢?
ThreadLocal类有一个map属性,map里面key存放当前线程对象,value存放线程局部变量副本,用线程对象做key来做区分,这样就能为每一个线程都提供一份独立的线程局部变量副本了
下面是一个简单的ThreadLocal类的实现:

/**  * TheadLocal类的简单实现  */ class SimpleThreadLocal {  private Map<Thread, Object> map = Collections  .synchronizedMap(new HashMap<Thread, Object>());  // 设置当前线程的线程局部变量  public void set(Object value) {  map.put(Thread.currentThread(),value);  }  // 获取当前线程的线程局部变量  public Object get() {  Object value_=map.get(Thread.currentThread());  //未维护当前线程存储线程局部变量  if(value_==null&&!map.containsKey(Thread.currentThread())){  value_=initialValue();  //完成维护  map.put(Thread.currentThread(), value_);  }  return value_;  }  // 删除当前线程的本地局部变量  public void remove() {  map.remove(Thread.currentThread());  }  // 获取当前线程的线程局部变量的初始值 默认赋空值  protected Object initialValue() {  return null;  } } 

ThreadLocal实例:
共享资源类:

/**  * 共享对象类  *   * @author zhangjian_java  *   */ public class SequenceNum { //把共享资源通过ThreadLocal的initialVlue方法封装进ThreadLocal中 使ThreadLocal为每一个线程都提供一份独立的变量副本,从而解决了多线程环境下相同数据访问冲突问题;  private ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {  public Integer initialValue() {  // 设置线程局部变量的初始值  return 9;  }  };  public Integer getNextNum() {  // 改变线程的局部变量的值 ---此处在模拟改变当前线程的全局共享资源副本  threadLocal.set(threadLocal.get() + 1);  // 获取更新后的变量副本  return threadLocal.get();  } } 

线程类:

/**  * 线程类  *   * @author zhangjian_java  *   */ public class MyThread extends Thread {  // 有一个共享资源对象作为属性  private SequenceNum sequenceNum;  // 含参构造器  public MyThread(SequenceNum sequenceNum) {  this.sequenceNum = sequenceNum;  }  // 重写父类的run方法  // 在当前线程中操作共享资源,模拟多线程对共享资源的并发访问  public void run() {  for (int i = 0; i <= 3; i++) {  System.out.println("线程:" + Thread.currentThread().getName()  + "当前线程的变量副本:" + sequenceNum.getNextNum());  }  } } 

测试类:

@Test  public void testThreadLocal() {  // 创建一个共享资源类  SequenceNum sequenceNum = new SequenceNum();  // 创建多个线程 多个线程之间共享同一资源  MyThread myThread1 = new MyThread(sequenceNum);  // 创建多个线程  MyThread myThread2 = new MyThread(sequenceNum);  // 创建多个线程  MyThread myThread3 = new MyThread(sequenceNum);  // 启动多线程  myThread1.start();  myThread2.start();  myThread3.start();  } 

结果:

线程:Thread-0当前线程的变量副本:10 线程:Thread-1当前线程的变量副本:10 线程:Thread-1当前线程的变量副本:11 线程:Thread-1当前线程的变量副本:12 线程:Thread-1当前线程的变量副本:13 线程:Thread-0当前线程的变量副本:11 线程:Thread-0当前线程的变量副本:12 线程:Thread-0当前线程的变量副本:13 线程:Thread-2当前线程的变量副本:10 线程:Thread-2当前线程的变量副本:11 线程:Thread-2当前线程的变量副本:12 线程:Thread-2当前线程的变量副本:13 

三个线程虽然并发的访问同一资源sequenceNum,但各个线程均有条不紊产生独立的序列号,没有对其他造成影响,
说明ThreadLoca为每一个当前线程提供一个独立的共享资源副本;
ThreadLocal和Thread同步机制的比较:
两者都是为了解决多线程下相同变量的访问冲突问题,
ThreadLocal:
提供共享资源副本,保证不破坏原数据,同一时间上允许有多个线程访问;
把共享资源通过ThreadLocal的initialVlue方法封装进ThreadLocal中 使ThreadLocal为每一个线程都提供一份独立的变量副本,从而解决了多线程环境下相同数据访问冲突问题;
Thread的同步机制:
从访问时间点控制访问数量为1;
使用对象的锁机制,使同一时间内只有一个线程访问共享资源;
ThreadLocal以空间换时间,访问并行化,对象独享化,为每一个线程都提供一份独立的变量副本,多个线程可以同时访问而互不影响;
Thread同步机制以时间换空间,访问串行化,对象共享化,仅提供一份变量,不同的线程排队访问;

原创粉丝点击