ThreadLocal

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同步机制以时间换空间,访问串行化,对象共享化,仅提供一份变量,不同的线程排队访问;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值