zoukankan      html  css  js  c++  java
  • 浅谈 ThreadLocal

    有时,你希望将每个线程数据(如用户ID)与线程关联起来。尽管可以使用局部变量来完成此任务,但只能在本地变量存在时才这样做。也可以使用一个实例属性来保存这些数据,但是这样就必须处理线程同步问题。幸运的是,Java提供了java.lang.ThreadLocal类是一个简单(而且非常方便)的选择。

    每个ThreadLocal实例都描述了一个线程局部变量,它是一个变量,它为每个访问变量的线程提供一个单独的存储槽(storage slot)。你可以将线程局部变量看作是一个多槽变量,其中每个线程可以在同一个变量中存储不同的值。每个线程只看到它的值,并且不知道其他线程在这个变量中有自己的值。

    一般将ThreadLocal声明为ThreadLocal <T>,其中T标识存储在该变量中的值的类型。 这个类声明了下面的构造方法和方法:

    • ThreadLocal():创建一个新的线程局部变量。
    • T get():返回调用线程存储槽中的值。 如果线程调用此方法时不存在条目,则get()方法调用initialValue()方法。
    • T initialValue():创建调用线程的存储槽并在此槽中存储初始(默认)值。 初始值默认为null。 必须子类化ThreadLocal并重写此受保护的方法以提供更合适的初始值。
    • void remove():删除调用线程的存储槽。如果这个方法后面是get(),而没有插入set()方法,则get()调用initialValue()
      * void set(T value):将调用线程的存储槽的值设置为value

    下面代码显示了如何使用ThreadLocal将不同的用户ID与两个线程相关联的示例。

    package concurrency;
    
    public class ThreadLocalDemo {
    	private static volatile ThreadLocal<String> userID = new ThreadLocal<String>();
    
    	public static void main(String[] args) {
    		Runnable task = () -> {
    			String name = Thread.currentThread().getName();
    			if (name.equals("A")) {
    				userID.set("yaya");
    			} else {
    				userID.set("maomao");
    			}
    			System.out.println(name + " " + userID.get());
    
    		};
    
    		Thread threadA = new Thread(task);
    		threadA.setName("A");
    		Thread threadB = new Thread(task);
    		threadB.setName("B");
    
    		threadA.start();
    		threadB.start();
    	} // end method main
    } // end class ThreadLocalDemo
    

    在实例化ThreadLocal并将引用分配给名为userID的volatile实例属性(该属性是volatile的,因为它是由不同的线程访问,这可能在多处理器/多核机器上执行——也可以指定为final),默认主线程创建两个线程在userID中存储不同的String对象并输出它们的值。

    在Eclipse运行此程序,输出的结果为:

    A yaya
    B maomao
    

    存储在线程局部变量中的值都互不相关的。 当一个新的线程被创建时,它会得到一个包含initialValue()值的新的存储槽。 也许你想从父线程(创建另一个线程的线程)传递值到子线程(创建的线程)。 则可以使用InheritableThreadLocal完成此任务。

    InheritableThreadLocalThreadLocal的一个子类。 除了声明的InheritableThreadLocal()构造方法外,该类声明以下受保护的方法:

    • T childValue(T parentValue):在创建子线程时,计算这个可继承线程局部变量的子线程的初始值,将该值作为父线程值的一个函数。在启动子线程之前,从父线程内部调用此方法。该方法仅返回其输入变量,如果所需要的是其他行为,则应该重写此方法。

    下面代码显示了如何使用InheritableThreadLocal将父线程的Integer对象传递给子线程。

    public class InheritableThreadLocalDemo {
        private static final InheritableThreadLocal<Integer> intVal = new InheritableThreadLocal<Integer>();
    
        public static void main(String[] args) {
            Runnable rP = () -> {
                intVal.set(new Integer(10));
                Runnable rC = () -> {
                    Thread thd = Thread.currentThread();
                    String name = thd.getName();
                    System.out.printf("%s %d%n", name, intVal.get());
                };
                Thread thdChild = new Thread(rC);
                thdChild.setName("Child");
                thdChild.start();
            };
    
            new Thread(rP).start();
        }
    }
    

    实例化InheritableThreadLocal并将其分配给final的实例属性(也可以使用volatile替代)intVal后,默认主线程创建一个父线程,该线程存储包含intVal中值的10的Integer对象。 父线程创建一个子线程,它访问intVal并检索其父线程的Integer对象。

    执行此程序,其运行结果如下:

    Child 10
    
  • 相关阅读:
    Tx.Origin 用作身份验证
    区块链夺宝合约
    (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
    Oracle查看用户密码过期,修改永不过期
    springboot测试
    (转)Springboot定时任务
    书单
    (转)redis分布式锁-SETNX实现
    mybatis-generator 代码自动生成工具
    (转)通过Ajax使用FormData对象无刷新上传文件
  • 原文地址:https://www.cnblogs.com/IcanFixIt/p/8707608.html
Copyright © 2011-2022 走看看