本文共 4102 字,大约阅读时间需要 13 分钟。
import java.util.Random;import java.util.concurrent.TimeUnit;public class Temp_4 { public static void main(String[] args) throws InterruptedException { Person person = new Person("小李子"); System.out.println(person.getName() + " begin to do"); int threadNum = 10; Thread[] workers = new Thread[threadNum]; for(int i = 0;i < threadNum;i++) { workers[i] = new Thread(new Runnable() { @Override public void run() { person.toDoSomething(); } }, "t-" + i); } for(int i = 0;i < threadNum;i++) { workers[i].start(); } for(int i = 0;i < threadNum;i++) { workers[i].join(); } System.out.println("all works done."); }}class Person { private static ThreadLocalcostTime = new ThreadLocal (); private static Random random = new Random(); private final String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void toDoSomething() { costTime.set(System.currentTimeMillis()); // 上班打卡 String threadName = Thread.currentThread().getName(); int sleepTime = random.nextInt(1001); while(sleepTime == 0) { sleepTime = random.nextInt(1001); } try { TimeUnit.MILLISECONDS.sleep(sleepTime); // 小李子只想睡觉 } catch (InterruptedException e) { e.printStackTrace(); } // 打卡下班 System.out.println(threadName + " command " + name + " to do something, cost time is " + (System.currentTimeMillis() - costTime.get()) / 1000.0 + "s"); }}
执行结果如下:
小李子 begin to dot-1 command 小李子 to do something, cost time is 0.083st-2 command 小李子 to do something, cost time is 0.193st-6 command 小李子 to do something, cost time is 0.396st-9 command 小李子 to do something, cost time is 0.41st-7 command 小李子 to do something, cost time is 0.461st-5 command 小李子 to do something, cost time is 0.488st-3 command 小李子 to do something, cost time is 0.494st-8 command 小李子 to do something, cost time is 0.575st-4 command 小李子 to do something, cost time is 0.668st-0 command 小李子 to do something, cost time is 0.803sall works done.
查看程序的运行结果,从中我并没有看出什么(-_- !),所以还是去看一眼 ThreadLocal 类的具体实现。(猜想:我们知道,ThreadLocal 可以实现:为每个线程保存一个对象,所以我猜测它的内部必定有一个数据结构/容器来维护每个线程及其持有的对象,该数据结构/容器要保证多线程安全,而且当线程终止后,ThreadLocal 最好还能够释放对该线程及其所持对象的引用)
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
其中 ThreadLocalMap 为一个自定义的 hashmap 实现(暂时不要关注它的实现细节),其目的是存放键值对 <ThreadLocal,value> 即每个 ThreadLocal 实例关联一个值 value,且线程类 Thread 中声明了一个 ThreadLocalMap 类型属性,用于保存属于该线程的 ‘线程本地变量’。我们知道,对于任一线程,一个 ThreadLocal 对象只关联一个值,而一个线程可以拥有多个 ThreadLocal 实例,所以就可以清晰:每个线程都有一个 hashmap 即 ThreadLocalMap 实例,用来保存属于该线程的 ThreadLocal 实例以及与该 ThreadLocal 实例对应的值 value。注意,每个线程都在一个属于它自己的 hashmap 对象上进行( get/set)操作,所以这里并不会有线程安全问题。当多个线程使用/操作同一个 ThreadLocal 实例时,相当于:有多个 hashmap 实例,使用同一个对象(即 ThreadLocal 实例)作为 key,来关联它们各自的 value 值。
综上,我是为了了解 ThreadLocal 的实现机制,所以从 ThreadLocal 类本身开始下手,但是直到看 ThreadLocalMap 以及线程类 Thread 中的 ThreadLocalMap 类型的属性域,我才恍然大悟:我搞错了顺序,我一开始就进入到了最里面,从而困惑于 ThreadLocal 的实现细节。我认为对 ThreadLocal 的理解,应当从 Thread 类中的 ThreadLocalMap 类型的属性开始:每个线程都有个一 ThreadLocalMap 类型的 hashmap 域,用于维护以 ThreadLocal 对象为 key 并以任意对象为 value 的键值对。所以再看 ThreadLocal,其仅仅用于创建一个普通的对象,该对象没有状态,只是作为 hashmap 的 key 而存在,至于它被多个线程使用,这一点并不特别。(还有一点就是 ThreadLocal 类本身,我认为这个类被设计的比较复杂,因为该类内部还隐藏了其它 3 个类,最关键的就是 ThreadLocalMap 类了,ThreadLocalMap 以它的包装类 ThreadLocal 作为 key,其自身却藏在 ThreadLocal 里面,可见类设计者有意隐藏 ThreadLocal 的实现细节,使得可以像使用普通对象一样对待 ThreadLocal 实例。注意,ThreadLocalMap 虽然定义在 ThreadLocal 的内部,但是它被定义为内部静态类,这和单独定义它其实效果一样,这一点可以简化对 ThreadLocal 的理解。)转载地址:http://wjlsi.baihongyu.com/