美文网首页
ThreadLocal

ThreadLocal

作者: 码农历险记 | 来源:发表于2017-08-12 15:58 被阅读0次

    现在,并发应用程序最关键的方面之一是共享数据。当你创建线程实现Runnable接口,然后开始各种线程对象使用相同的Runnable对象,所有线程共享,Runnable对象内部定义相同的属性。这本质上意味着,如果您更改了线程中的任何属性,所有线程都将受到此更改的影响,并将通过第一个线程看到修改后的值。有时它是你希望的行为,例如多个线程增加/减少相同的计数器变量;但有时您希望确保每个线程都必须工作在自己的线程实例副本上,并且不影响其他数据。

    ThreadLocal

    每个线程的Thread对象中都有一个ThreadLocalMap对象,这个对象存储来一组以ThreadLocal.threadLocalHashCode为键,以本地线程变量为值的K-V值对,ThreadLocal对象就是当前线程的ThreadLocalMap的访问入口,每一个ThreadLocal对象都包含独一无二的threadLocalHashCode值,使用这个值就可以在线程K-V值中终会对应的本地线程变量。

    何时使用ThreadLocal

    比如,你正在从事电子商务应用程序,需要为每个客户请求生成一个唯一的交易ID,控制器进程需要将此交易ID传递给Manager / DAO类中的业务方法,以便进行日志记录。一个解决方案可能是将此交易ID作为参数传递给所有业务方法。但这不是一个好的解决方案,因为代码是多余的和不必要的。

    为了解决这个问题,在这里你可以使用ThreadLocal变量。你可以在控制器或任何预处理器拦截交易ID,并设置此交易ID到ThreadLocal里。在这之后,不管该控制器调用什么方法,它们都可以从ThreadLocal访问此交易ID。请注意,应用程序控制器将在一次服务多个请求,因为每个请求在单独的线程中在框架级别处理,交易ID将是每个线程唯一的,并且将从线程的执行路径中访问。

    ThreadLocal API介绍

    Java Concurrency API为ThreadLocal变量的使用提供良好的机制和优秀的性能。

    public class ThreadLocal<T> extends Object {...}

    该类提供线程本地变量。这些变量与一般的变量不同,每个线程访问一个线程(通过get或set方法)有自己独立的变量初始化副本。ThreadLocal实例通常是私有的静态字段在类希望关联状态的线程(例如,一个用户ID或交易ID)

    这个类有以下方法:
    get():返回该线程局部变量的当前线程的值复制。
    initialvalue():返回该线程局部变量的当前线程的“初始值”。
    remove():删除该线程局部变量的当前线程的值。
    set(T value):将当前线程的本地线程变量的副本设置为指定的值。

    如何使用ThreadLocal

    下面的例子使用了两个线程局部变量,即threadId和startDate。它们都被定义为建议的“私有静态”字段。threadId将被用来确定当前正在运行的线程和startDate用来表示启动线程的执行的时间。以上信息将打印在控制台中,以验证每个线程是否保留了自己的变量副本。

    public class DemoTask implements Runnable {
        private static final AtomicInteger nextId = new AtomicInteger(0);
        private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return nextId.getAndIncrement();
            }
        };
    
        public static int getThreadId() {
            return threadId.get();
        }
    
        private static final ThreadLocal<Date> startDate = new ThreadLocal<Date>() {
            @Override
            protected Date initialValue() {
                return new Date();
            }
        };
    
    
        @Override
        public void run() {
            System.out.printf("Starting Thread: %s : %s\n", getThreadId(), startDate.get());
            try {
                TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("Thread Finished: %s : %s\n", getThreadId(), startDate.get());
        }
    
        public static void main(String[] args) {
            DemoTask demoTask = new DemoTask();
    
            Thread thread1 = new Thread(demoTask);
            Thread thread2 = new Thread(demoTask);
            Thread thread3 = new Thread(demoTask);
    
            thread1.start();
            thread2.start();
            thread3.start();
    
            try {
                thread1.join();
                thread2.join();
                thread3.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    现在来验证变量基本上能够保持自己的状态,不论多初始化为多少线程。我们创建了该任务的三个实例;启动线程;然后验证信息在控制台打印它们。

    Starting Thread: 0 : Sat Aug 12 15:38:30 CST 2017
    Starting Thread: 2 : Sat Aug 12 15:38:30 CST 2017
    Starting Thread: 1 : Sat Aug 12 15:38:30 CST 2017
    Thread Finished: 0 : Sat Aug 12 15:38:30 CST 2017
    Thread Finished: 2 : Sat Aug 12 15:38:30 CST 2017
    Thread Finished: 1 : Sat Aug 12 15:38:30 CST 2017
    

    在上面的输出中,打印语句的顺序每次都会不同。我们可以清楚地看到ThreadLocal值为每个线程实例所保管。

    最常见的ThreadLocal使用是当您有一些对象不是线程安全的,但您希望避免使用同步关键字/块同步访问该对象。相反,给每个线程自己的对象实例来工作。
    一个很好的替代synchronization(同步)或ThreadLocal是使用局部变量。局部变量始终是线程安全的。唯一阻止你这样做的是应用程序设计约束。

    在webapp服务器,它可能保持一个线程池,所以一个ThreadLocal变量应该在响应客户端请求前删除,因为当前线程可以被下一个请求重复使用。另外,如果你当你完成请求不清理的时候,任何引用它加载的类将保持在永久堆作为部署webapp的一部分,并永远不会被垃圾回收。

    InheritableThreadLocal

    InheritableThreadLocal类是ThreadLocal的子类。为了解决ThreadLocal实例内部每个线程都只能看到自己的私有值,所以InheritableThreadLocal允许一个线程创建的所有子线程访问其父线程的值。
    参考
    1.Java ThreadLocal Variables – When and How to Use?
    2.Java TheadLocal

    相关文章

      网友评论

          本文标题:ThreadLocal

          本文链接:https://www.haomeiwen.com/subject/iymdrxtx.html