• ThreadLocal小记


    API:

    public class ThreadLocal<T>
         
    extends Object

    该类提供了线程局部 (thread-local) 变量。

    这些变量不同于它们的普通相应物。由于訪问某个变量(通过其 getset 方法)的每一个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal 实例一般是类中的 private static 字段,它们希望将状态与某一个线程(比如,用户 ID 或事务 ID)相关联


    每一个线程都保持对其线程局部变量副本的隐式引用,仅仅要线程是活动的而且 ThreadLocal 实例是可訪问的。在线程消失之后。其线程局部实例的全部副本都会被垃圾回收(除非存在对这些副本的其它引用)。


    Method:

    initialValue

    protected T initialValue()
    返回此线程局部变量的当前线程的“初始值”。

    线程第一次使用 get() 方法訪问变量时将调用此方法,但假设线程之前调用了 set(T) 方法。则不会对该线程再调用 initialValue 方法。

    通常,此方法对每一个线程最多调用一次。但假设在调用get() 后又调用了 remove(),则可能再次调用此方法。

    该实现返回 null;假设程序猿希望线程局部变量具有 null 以外的值。则必须为 ThreadLocal 创建子类。并重写此方法。通常将使用匿名内部类完毕此操作。

    返回:
    返回此线程局部变量的初始值


    get

    public T get()
    返回此线程局部变量的当前线程副本中的值。假设变量没实用于当前线程的值,则先将其初始化为调用 initialValue() 方法返回的值。
    返回:
    此线程局部变量的当前线程的值


    set

    public void set(T value)
    将此线程局部变量的当前线程副本中的值设置为指定值。大部分子类不须要重写此方法,它们仅仅依靠 initialValue() 方法来设置线程局部变量的值。
    參数:
    value - 存储在此线程局部变量的当前线程副本中的值。


    remove

    public void remove()
    移除此线程局部变量当前线程的值。

    假设此线程局部变量随后被当前线程 读取,且这期间当前线程没有 设置其值。则将调用其 initialValue() 方法又一次初始化其值。

    这将导致在当前线程多次调用 initialValue 方法。


    例1:

    package com.example;
    
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    public class SafeTask implements Runnable{
    	private static ThreadLocal<Date> startDate= new ThreadLocal<Date>() {
    	    protected Date initialValue(){
    	      return new Date();
    	    }
    	  };
    		
    	
    	@Override
    	public void run() {
    		
    	    System.out.printf("Starting Thread: %s : %s
    ",Thread.currentThread().getId(),startDate.get());
    	    try {
    	      TimeUnit.SECONDS.sleep( (int)Math.rint(Math.random()*10));
    	    } catch (InterruptedException e) {
    	      e.printStackTrace();
    	    }
    	    System.out.printf("Thread Finished: %s : %s
    ",Thread.currentThread().getId(),startDate.get());
    		
    	}
    	
    	public static void main(String[] args) {
    		SafeTask task=new SafeTask();
    	    for (int i=0; i<3; i++){
    	      Thread thread=new Thread(task);
    	      thread.start();
    	      try {
    	        TimeUnit.SECONDS.sleep(2);
    	      } catch (InterruptedException e) {
    	        e.printStackTrace();
    	      }
    	    }
    	  }
    
    }
    

    执行结果:

    Starting Thread: 15 : Mon Nov 17 13:36:33 CST 2014
    Starting Thread: 17 : Mon Nov 17 13:36:35 CST 2014
    Thread Finished: 17 : Mon Nov 17 13:36:35 CST 2014
    Starting Thread: 18 : Mon Nov 17 13:36:37 CST 2014
    Thread Finished: 15 : Mon Nov 17 13:36:33 CST 2014
    Thread Finished: 18 : Mon Nov 17 13:36:37 CST 2014


    从结果,能够看到,三个线程各自存储和訪问各自维护的startDate局部变量。


    解析:

    ThreadLocal有一个内部静态类ThreadLocalMap来管理线程中的变量。能够简单的将其理解成一个Map。

    而Map中存放的指的方式是:用当前的线程来做为KEY。线程相应的变量值作为VALUE。

    当某一线程要获取当前变量的值时,就使用ThreadLocal.get()方法。通过线程自身作为KEY,去ThreadLocalMap中查找相应的值。

    这样就能够解释“每一个线程都保持对其线程局部变量副本的隐式引用”。

    而为了使每一个线程都能够使用该变量的副本使用,ThreadLocal 实例一般是类中的 private static 字段”。



    为了更好的理解ThreadLocal这样的机制,请看以下的样例。

    例2:

    package com.example;
    
    import java.util.Date;
    import java.util.concurrent.TimeUnit;
    
    public class SafeTask implements Runnable{
    
    	private static ThreadLocal<Date> startDate = new ThreadLocal<Date>();
    		
    	
    	@Override
    	public void run() {
    	    startDate = new ThreadLocal<Date>();
    	    startDate.set(new Date());
    	    System.out.printf("Thread: %s new startDate.
    ", Thread.currentThread().getId());
    	    System.out.printf("Starting Thread: %s : %s
    ",Thread.currentThread().getId(),startDate.get());
    	    try {
    	      TimeUnit.SECONDS.sleep( (int)Math.rint(Math.random()*10));
    	    } catch (InterruptedException e) {
    	      e.printStackTrace();
    	    }
    	    System.out.printf("Thread Finished: %s : %s
    ",Thread.currentThread().getId(),startDate.get());
    		
    	}
    	
    	public static void main(String[] args) {
    		SafeTask task=new SafeTask();
    	    for (int i=0; i<3; i++){
    	      Thread thread=new Thread(task);
    	      thread.start();
    	      try {
    	        TimeUnit.SECONDS.sleep(2);
    	      } catch (InterruptedException e) {
    	        e.printStackTrace();
    	      }
    	    }
    	  }
    
    }
    

    运行结果:

    Thread: 15 new startDate.
    Starting Thread: 15 : Mon Nov 17 13:55:16 CST 2014
    Thread: 17 new startDate.
    Starting Thread: 17 : Mon Nov 17 13:55:18 CST 2014
    Thread Finished: 15 : null
    Thread Finished: 17 : Mon Nov 17 13:55:18 CST 2014
    Thread: 18 new startDate.
    Starting Thread: 18 : Mon Nov 17 13:55:20 CST 2014
    Thread Finished: 18 : Mon Nov 17 13:55:20 CST 2014
    

    为什么例2的结果中会出现null呢?

    原因就在于,代码在线程中又一次创建来ThreadLocal

    startDate = new ThreadLocal<Date>();
    这样做,就导致了startDate指向了新的ThreadLocal对象。那么之前存放在当中的副本就丢失了,所以才会出现null的情况。


    通过以上的样例,我们就能理解为什么ThreadLocal 实例一般是类中的 private static 字段

    我们须要明确。每一个线程中变量的副本,是通过线程的KEY和变量的VALUE存放在ThreadLocalMap中,而不是说。为每一个线程建立ThreadLocal。

    就类而言,他实际仅仅维护和管理着一个ThreadLocal。


    PS:代码部分截取来自《Java 7 Concurrency Cookbook》

  • 相关阅读:
    vue中动态数据使用wowjs显示动画
    vue 切换路由页面不在最顶部
    dp,.单词的划分
    二分建火车站
    .最大上升子序列和
    饥饿的奶牛(不重区间最大值)
    F. 1.小W 的质数(prime)(欧拉筛)
    月月给华华出题
    积性函数
    垒石头(排序+dp)
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5278715.html
Copyright © 2020-2023  润新知