JAVA基础-ThreadLocal分析

来源:互联网 发布:android python kivy 编辑:程序博客网 时间:2024/05/21 08:41

ThreadLocal分析

[LOC]

ThreadLocal简介

JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序,ThreadLocal并不是一个Thread,而是Thread的局部变量。
—–百度百科

非ThreadLocal和ThreadLocal对比

非ThreadLocal

//A.javapublic class A {    String name = "initvalue:";    public static void main(String[] args) {        A a = new A();        Thread t1 = new Thread(new T(a));        Thread t2 = new Thread(new T(a));        t1.start();        t2.start();    }}
//T.javapublic class T implements Runnable {    A a;    public T(A a) {        this.a =a ;    }    @Override    public void run() {        for(int i=0;i<4;i++)        {            a.name = a.name+i;            System.out.println(Thread.currentThread().getName()+" "+a.name);        }    }}

运行结果如下:

Thread-0 initvalue:0
Thread-0 initvalue:01
Thread-1 initvalue:0
Thread-1 initvalue:0121
Thread-0 initvalue:012
Thread-0 initvalue:012123
Thread-1 initvalue:01212
Thread-1 initvalue:0121233
因为两个线程共享一个对象a导致了这种线程不安全的结果,大致流程如下

Created with Raphaël 2.1.0t1t1aat2t2a.name=a.name+ia.name=initvalue:0a.name=a.name+ia.name=initvalue:01syso(name)syso(name)

非hreadLocal

//A.javapublic class A {    ThreadLocal<String> name = new ThreadLocal<String>(){        @Override        protected String initialValue() {            return "initvalue:";        };    };    public static void main(String[] args) {        A a = new A();        Thread t1 = new Thread(new T(a));        Thread t2 = new Thread(new T(a));        t1.start();        t2.start();    }}
//T.javapublic class T implements Runnable {    A a;    public T(A a) {        this.a =a ;    }    @Override    public void run() {        for(int i=0;i<4;i++)        {            a.name.set(a.name.get()+i);            System.out.println(Thread.currentThread().getName()+" "+a.name.get());        }    }}

运行结果如下:

Thread-0 initvalue:0
Thread-1 initvalue:0
Thread-0 initvalue:01
Thread-0 initvalue:012
Thread-0 initvalue:0123
Thread-1 initvalue:01
Thread-1 initvalue:012
Thread-1 initvalue:0123

流程如下:

Created with Raphaël 2.1.0t1t1(t1)a(t1)at2t2(t2)a(t2)aa.name=a.name+ia.name=initvalue:0a.name=a.name+ia.name=initvalue:0syso(name)syso(name)

源码分析

在调用a.name.get()的时候做了如下操作,见java\lang\ThreadLocal.java

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();}

这里通过getMap(t)获取当前线程的threadLocals该变量默认为null

ThreadLocalMap getMap(Thread t) {     return t.threadLocals; }

threadLocalsnull
则调用setInitialValue()来初始化该变量

private T setInitialValue() {    T value = initialValue();    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);    return value;}

这里调用 createMap(t, value)来创建当前线程的ThreadLocalMap变量

 void createMap(Thread t, T firstValue) {     t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {     table = new Entry[INITIAL_CAPACITY];     int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);     table[i] = new Entry(firstKey, firstValue);     size = 1;     setThreshold(INITIAL_CAPACITY); }

这里将ThreadLocal变量作为key,value为值(name,”initvalue:”)保存在table[i]中
这个i是通过threadLocalHashCode来计算的
让我们来看看这个threadLocalHashCode是如何得来的

    private final int threadLocalHashCode = nextHashCode();    /**     * The next hash code to be given out. Updated atomically. Starts at     * zero.     */    private static AtomicInteger nextHashCode =        new AtomicInteger();    /**     * The difference between successively generated hash codes - turns     * implicit sequential thread-local IDs into near-optimally spread     * multiplicative hash values for power-of-two-sized tables.     */    private static final int HASH_INCREMENT = 0x61c88647;

threadLocalHashCode使用过静态变量nextHashCode计算得来的
也就是说每个ThreadLocal变量的nextHashCode值都是不一样的
我们再来看看上面的get()方法

  if (map != null) {        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;        }    }
  private Entry getEntry(ThreadLocal<?> key) {      int i = key.threadLocalHashCode & (table.length - 1);      Entry e = table[i];      if (e != null && e.get() == key)          return e;      else          return getEntryAfterMiss(key, i, e);  }

这里也是通过该变量的threadLocalHashCode来获取变量存储的位置并返回变量的
这样整个流程就理通了
下面展示了ThreadLocal的草图
这里写图片描述

0 0
原创粉丝点击