• ThreadLocal


    ThreadLocal入门

      ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。

      ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

      ThreadLocal 适用于变量在线程间隔离而在方法或类间共享的场景。

    使用示例:

     1 public class ThreadLocalDemo {
     2   public static void main(String[] args) throws InterruptedException {
     3     int threads = 3;
     4     CountDownLatch countDownLatch = new CountDownLatch(threads);
     5     InnerClass innerClass = new InnerClass();
     6     for(int i = 1; i <= threads; i++) {
     7       new Thread(() -> {
     8         for(int j = 0; j < 4; j++) {
     9           innerClass.add(String.valueOf(j));
    10           innerClass.print();
    11         }
    12         innerClass.set("hello world");
    13         countDownLatch.countDown();
    14       }, "thread - " + i).start();
    15     }
    16     countDownLatch.await();
    17   }
    18   private static class InnerClass {
    19     public void add(String newStr) {
    20       StringBuilder str = Counter.counter.get();
    21       Counter.counter.set(str.append(newStr));
    22     }
    23     public void print() {
    24       System.out.printf("Thread name:%s , ThreadLocal hashcode:%s, Instance hashcode:%s, Value:%s
    ",
    25       Thread.currentThread().getName(),
    26       Counter.counter.hashCode(),
    27       Counter.counter.get().hashCode(),
    28       Counter.counter.get().toString());
    29     }
    30     public void set(String words) {
    31       Counter.counter.set(new StringBuilder(words));
    32       System.out.printf("Set, Thread name:%s , ThreadLocal hashcode:%s,  Instance hashcode:%s, Value:%s
    ",
    33       Thread.currentThread().getName(),
    34       Counter.counter.hashCode(),
    35       Counter.counter.get().hashCode(),
    36       Counter.counter.get().toString());
    37     }
    38   }
    39   private static class Counter {
    40     private static ThreadLocal<StringBuilder> counter = new ThreadLocal<StringBuilder>() {
    41       @Override
    42       protected StringBuilder initialValue() {
    43         return new StringBuilder();
    44       }
    45     };
    46   }
    47 }

    输出:

     1 Thread name:thread - 1 , ThreadLocal hashcode:372282300, Instance hashcode:418873098, Value:0
     2 Thread name:thread - 3 , ThreadLocal hashcode:372282300, Instance hashcode:1609588821, Value:0
     3 Thread name:thread - 2 , ThreadLocal hashcode:372282300, Instance hashcode:1780437710, Value:0
     4 Thread name:thread - 3 , ThreadLocal hashcode:372282300, Instance hashcode:1609588821, Value:01
     5 Thread name:thread - 1 , ThreadLocal hashcode:372282300, Instance hashcode:418873098, Value:01
     6 Thread name:thread - 3 , ThreadLocal hashcode:372282300, Instance hashcode:1609588821, Value:012
     7 Thread name:thread - 3 , ThreadLocal hashcode:372282300, Instance hashcode:1609588821, Value:0123
     8 Set, Thread name:thread - 3 , ThreadLocal hashcode:372282300,  Instance hashcode:1362597339, Value:hello world
     9 Thread name:thread - 2 , ThreadLocal hashcode:372282300, Instance hashcode:1780437710, Value:01
    10 Thread name:thread - 1 , ThreadLocal hashcode:372282300, Instance hashcode:418873098, Value:012
    11 Thread name:thread - 2 , ThreadLocal hashcode:372282300, Instance hashcode:1780437710, Value:012
    12 Thread name:thread - 1 , ThreadLocal hashcode:372282300, Instance hashcode:418873098, Value:0123
    13 Thread name:thread - 2 , ThreadLocal hashcode:372282300, Instance hashcode:1780437710, Value:0123
    14 Set, Thread name:thread - 1 , ThreadLocal hashcode:372282300,  Instance hashcode:482932940, Value:hello world
    15 Set, Thread name:thread - 2 , ThreadLocal hashcode:372282300,  Instance hashcode:1691922941, Value:hello world
    • 从第1-3行输出可见,每个线程通过 ThreadLocal 的 get() 方法拿到的是不同的 StringBuilder 实例
    • 第1-3行输出表明,每个线程所访问到的是同一个 ThreadLocal 变量
    • 对比第1行与第15行输出并结合第38行代码可知,使用 set(T t) 方法后,ThreadLocal 变量所指向的 StringBuilder 实例被替换

    ThreadLocal原理

     1 public T get() {
     2   Thread t = Thread.currentThread();
     3   ThreadLocalMap map = getMap(t);
     4   if (map != null) {
     5     ThreadLocalMap.Entry e = map.getEntry(this);
     6     if (e != null) {
     7       @SuppressWarnings("unchecked")
     8       T result = (T)e.value;
     9       return result;
    10     }
    11   }
    12   return setInitialValue();
    13 }

      2,3行取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。每一个线程都有一个ThreadLocalMap。

      ThreadLocalMap的键是this,也就是调用get()的ThreadLocal对象,值是实例。所以不同线程拿到的实例也是不同的。

      

    1 static class Entry extends WeakReference<ThreadLocal<?>> {
    2   /** The value associated with this ThreadLocal. */
    3   Object value;
    4   Entry(ThreadLocal<?> k, Object v) {
    5     super(k);
    6     value = v;
    7   }
    8 }

      与 HashMap 不同的是,ThreadLocalMap 的每个 Entry 都是一个对  的弱引用,这一点从super(k)可看出。另外,每个 Entry 都包含了一个对  的强引用。

      使用弱引用的原因在于,当没有强引用指向 ThreadLocal 变量时,它可被回收,从而避免上文所述 ThreadLocal 不能被回收而造成的内存泄漏的问题。

      但是,这里又可能出现另外一种内存泄漏的问题。ThreadLocalMap 维护 ThreadLocal 变量与具体实例的映射,当 ThreadLocal 变量被回收后,该映射的键变为 null,该 Entry 无法被移除。从而使得实例被该 Entry 引用而无法被回收造成内存泄漏。

    适用场景:

    • 每个线程需要有自己单独的实例
    • 实例需要在多个方法中共享,但不希望被多线程共享

    转自:http://www.jasongj.com/java/threadlocal/

  • 相关阅读:
    HDU 1018 Big Number
    UVa 10603 倒水问题
    UVa 12108 特别困的学生
    UVa 1590 IP网络(简单位运算)
    UVa 12325 宝箱
    UVa 253 骰子涂色
    UVa 815 洪水!
    springmvc(2)处理器设配器和映射器
    SpringMVC(1)
    MyBatis(10)逆向工程
  • 原文地址:https://www.cnblogs.com/mengchunchen/p/9908168.html
Copyright © 2020-2023  润新知