`

Java Concurrent之 ThreadLocal

 
阅读更多

   之前的ThreadLocal的实现方式是声明一个HashTable,以Thread.currentThread()为key,变量的拷贝为value。而现在ThreadLocal的实现方式有点儿改头换面了。

   

public class ThreadLocal<T> {
    /**
     * ThreadLocals rely on per-thread linear-probe hash maps attached
     * to each thread (Thread.threadLocals and
     * inheritableThreadLocals).  The ThreadLocal objects act as keys,
     * searched via threadLocalHashCode.  This is a custom hash code
     * (useful only within ThreadLocalMaps) that eliminates collisions
     * in the common case where consecutively constructed ThreadLocals
     * are used by the same threads, while remaining well-behaved in
     * less common cases.
     */
    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;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
	return nextHashCode.getAndAdd(HASH_INCREMENT); 
    }

    /**
     * Returns the current thread's "initial value" for this
     * thread-local variable.  This method will be invoked the first
     * time a thread accesses the variable with the {@link #get}
     * method, unless the thread previously invoked the {@link #set}
     * method, in which case the <tt>initialValue</tt> method will not
     * be invoked for the thread.  Normally, this method is invoked at
     * most once per thread, but it may be invoked again in case of
     * subsequent invocations of {@link #remove} followed by {@link #get}.
     *
     * <p>This implementation simply returns <tt>null</tt>; if the
     * programmer desires thread-local variables to have an initial
     * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
     * subclassed, and this method overridden.  Typically, an
     * anonymous inner class will be used.
     *
     * @return the initial value for this thread-local
     */
    protected T initialValue() {
        return null;
    }

    /**
     * Creates a thread local variable.
     */
    public ThreadLocal() {
    }
    从上述源码可以看出,ThreadLocal现在只有3个变量,当我们实例化一个ThreadLocal对象时,会调用nextHashCode()方法将nextHashCode的值赋值给实例的成员变量threadLocalHashCode;与此同时也会将nextHashCode的值增加HASH_INCREMENT。ThreadLocal就可利用这个变量来区分不同的ThreadLocal实例。

 

 

private static int nextHashCode() {
	return nextHashCode.getAndAdd(HASH_INCREMENT); 
    }

/**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the previous value
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
//如果当前的值(private static AtomicInteger nextHashCode)的值与current相等,               则将next赋值给当前值。
            if (compareAndSet(current, next))
                return current;
        }
    }

/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
	return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    接下来我们来看看ThreadLocal的get()方法:

 

 /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);//获取ThreadLocal内部类ThreadLocalMap对象 (在Thread中定义的threadLocals)ThreadLocal中的值其实交给了当前Thread保管,自己只保管自己的ThreadLocalHashCode变量。这样一旦线程退出,ThreadLocal将无法访问该线程。
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();//返回初始值
    }
/**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
 ThreadLocal内部类ThreadLocalMap:
static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;

        /**
         * The table, resized as necessary.
         * table.length MUST always be a power of two.
         */
        private Entry[] table;

        /**
         * The number of entries in the table.
         */
        private int size = 0;

        /**
         * The next size value at which to resize.
         */
        private int threshold; // Default to 0
   我们通过源码会发现ThreadLocalMap的Entry继承了WeakReference,众所周知,weakReference是一种比软引用还要弱的引用类型。在系统发生GC时,不管系统的堆空间是否富余,只要发现弱引用,都会将其回收。ThreadLocalMap中以数组的形式来存储Entry实例,并且Entry是以ThreadLocal为key,初始大小为16.
   ThreadLocalMap包含有两个构造器,使得ThreadLocalMap既可以通过传入ThreadLocal-value对的形式、也可以通过传入ThreadLocalMap实例的方式实例化ThreadLocalMap。使用第二种方式初始化ThreadLocalMap时,会将传入的ThreadLocalMap中的Entry依次添加到当前的ThreadLocalMap实例的table变量中。
/**
         * Construct a new map initially containing (firstKey, firstValue).
         * ThreadLocalMaps are constructed lazily, so we only create
         * one when we have at least one entry to put in it.
         */
        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);
        }

        /**
         * Construct a new map including all Inheritable ThreadLocals
         * from given parent map. Called only by createInheritedMap.
         *
         * @param parentMap the map associated with parent thread.
         */
        private ThreadLocalMap(ThreadLocalMap parentMap) {
            Entry[] parentTable = parentMap.table;
            int len = parentTable.length;
            setThreshold(len);
            table = new Entry[len];

            for (int j = 0; j < len; j++) {
                Entry e = parentTable[j];
                if (e != null) {
                    ThreadLocal key = e.get();
                    if (key != null) {
                        Object value = key.childValue(e.value);
                        Entry c = new Entry(key, value);
                        int h = key.threadLocalHashCode & (len - 1);
                        while (table[h] != null)
                            h = nextIndex(h, len);
                        table[h] = c;
                        size++;
                    }
                }
            }
        }

/**
         * Get the entry associated with key.  This method
         * itself handles only the fast path: a direct hit of existing
         * key. It otherwise relays to getEntryAfterMiss.  This is
         * designed to maximize performance for direct hits, in part
         * by making this method readily inlinable.
         *
         * @param  key the thread local object
         * @return the entry associated with key, or null if no such
         */
//通过实例变量threadLocalHashCode计算下标,然后返回对应的值 
//set 、remove方法类似
        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);
        }
  从上述ThreadLocal源码加上Thread的源码,我们会发现每个线程都持有一个自己的ThreadLocal.ThreadLocalMap变量。这样就方便每个线程持有一份自己的变量拷贝而互不干扰。
public
class Thread implements Runnable {
... ...
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.  
     */ 
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
/**
     * Initializes a Thread.
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize the desired stack size for the new thread, or
     *        zero to indicate that this parameter is to be ignored.
     */
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
	Thread parent = currentThread();
	SecurityManager security = System.getSecurityManager();
	if (g == null) {
	    /* Determine if it's an applet or not */
	    
	    /* If there is a security manager, ask the security manager
	       what to do. */
	    if (security != null) {
		g = security.getThreadGroup();
	    }

	    /* If the security doesn't have a strong opinion of the matter
	       use the parent thread group. */
	    if (g == null) {
		g = parent.getThreadGroup();
	    }
	}

	/* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
	g.checkAccess();

	/*
	 * Do we have the required permissions?
	 */
	if (security != null) {
	    if (isCCLOverridden(getClass())) {
	        security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
	    }
	}


        g.addUnstarted();

	this.group = g;
	this.daemon = parent.isDaemon();
	this.priority = parent.getPriority();
	this.name = name.toCharArray();
	if (security == null || isCCLOverridden(parent.getClass()))
	    this.contextClassLoader = parent.getContextClassLoader();
	else
	    this.contextClassLoader = parent.contextClassLoader;
	this.inheritedAccessControlContext = AccessController.getContext();
	this.target = target;
	setPriority(priority);
        if (parent.inheritableThreadLocals != null)
	    this.inheritableThreadLocals =
		ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        tid = nextThreadID();
    }

   /**
     * Allocates a new <code>Thread</code> object. This constructor has 
     * the same effect as <code>Thread(null, null,</code>
     * <i>gname</i><code>)</code>, where <b><i>gname</i></b> is 
     * a newly generated name. Automatically generated names are of the 
     * form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer. 
     *
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    public Thread() {
	init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    /**
     * Allocates a new <code>Thread</code> object. This constructor has 
     * the same effect as <code>Thread(null, target,</code>
     * <i>gname</i><code>)</code>, where <i>gname</i> is 
     * a newly generated name. Automatically generated names are of the 
     * form <code>"Thread-"+</code><i>n</i>, where <i>n</i> is an integer. 
     *
     * @param   target   the object whose <code>run</code> method is called.
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    public Thread(Runnable target) {
	init(null, target, "Thread-" + nextThreadNum(), 0);
    }
... ...
}
    实际上,Thread里定义了两个ThreadLocalMap变量,在init()方法里我们会看到对于inheritableThreadLocals变量的操作。
if (parent.inheritableThreadLocals != null)
	    this.inheritableThreadLocals =
		ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);


//调用ThreadLocalMap的传入ThreadLocalMap参数的构造器
 static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
        return new ThreadLocalMap(parentMap);
    }
   也就是说在Thread类中,当前线程回去调用init()方法去初始化一个线程,而在init方法调用过程中会执行将当前线程的inheritableThreadLocals的值拷贝给待初始化的线程。
   而在线程真正终止前会执行exit()方法,将threadLocals和inheritableThreadLocals变量都指向null。
 /**
     * This method is called by the system to give a Thread
     * a chance to clean up before it actually exits.
     */
    private void exit() {
	if (group != null) {
	    group.remove(this);
	    group = null;
	}
	/* Aggressively null out all reference fields: see bug 4006245 */
	target = null;
	/* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }
  综述:
    ThreadLocal为各个线程管理的变量副本都放在各自线程中的threadLocals变量中。ThreadLocal变量中保管只是一个ThreadLocalHashCode变量,这样在线程退出后是无法通过ThreadLocal变量访问这个线程的。(因为线程退出时会调用exit()方法,将threadLocals和inheritableThreadLocals都指向了null;而当ThreadLocal为null时,thread里的threadLocals也会移除key为ThreadLocal的Entry)。这种实现方式相比之前的HashTable,无需同步,大大提升了性能。
分享到:
评论

相关推荐

    Java中ThreadLocal的设计与使用

    应优先考虑其他同步机制,如`synchronized`关键字或`java.util.concurrent`包中的工具类。 2. **及时清理**:确保在不再使用ThreadLocal时调用`remove()`,以防止内存泄漏。 3. **谨慎使用在长生命周期线程中**:如...

    java concurrent 包 详细解析

    Java并发包(java.concurrent)是Java平台中处理多线程编程的核心工具包,它提供了丰富的类和接口,使得开发者能够高效、安全地编写多线程程序。这个包的设计目标是提高并发性能,减少同步代码的复杂性,并提供高级...

    java_util_concurrent中文版pdf

    《Java Util Concurrent中文版》是Java并发编程领域的重要参考资料,主要涵盖了Java标准库中的`java.util.concurrent`包及其相关类和接口。这个包是Java多线程编程的核心,提供了高效、安全的并发工具,帮助开发者...

    Java 并发编程之ThreadLocal详解及实例

    import java.util.concurrent.atomic.AtomicInteger; public class UniqueThreadIdGenerator { private static final AtomicInteger uniqueId = new AtomicInteger(0); private static final ThreadLocal...

    ThreadLocal

    import java.util.concurrent.atomic.AtomicInteger; public class ThreadId { // Atomic integer containing the next thread ID to be assigned private static final AtomicInteger nextId = new ...

    Java并发编程:设计原则与模式(Concurrent.Programming.in.Java)(中英版)

    例如,使用无锁数据结构或原子操作(`java.util.concurrent.atomic`包)。 3. **避免死锁、活锁和饥饿**:理解并预防这些并发问题至关重要。死锁发生在两个或多个线程相互等待对方释放资源导致僵局;活锁是线程不断...

    Java的concurrent包动画演示

    Java的`concurrent`包是Java多线程编程的核心组件,它包含了一系列高效、线程安全的类和接口,使得开发者能够更容易地处理并发问题。这个包中的工具和类是Java平台对并行编程的强大支持,它极大地提升了程序在多...

    concurrent 源代码

    在Java编程语言中,"concurrent"一词通常与多线程并发编程相关,它涉及到如何在多个执行线程之间高效、安全地共享资源。这里的"concurrent源代码"可能是指Java并发包(`java.util.concurrent`)中的源代码,这个包包含...

    concurrent-1.3.4-sources.jar

    这里提到的`concurrent-1.3.4-sources.jar`是一个特定版本的Java并发库源代码包,它包含了Java并发编程的核心组件和工具,对理解并发编程有着重要的参考价值。这个版本是1.3.4,虽然相比现代的Java版本可能有些过时...

    java5 并发包 (concurrent)思维导图

    Java 5并发包(`java.util.concurrent`,简称`Concurrent`包)是Java平台中用于多线程编程的重要组成部分,它提供了丰富的并发工具类,极大地简化了在多线程环境下的编程工作。这个包的设计目标是提高并发性能,减少...

    java 中ThreadLocal本地线程和同步机制的比较

    与传统的同步机制(如`synchronized`关键字或`java.util.concurrent`包中的工具)相比,ThreadLocal的优势在于它不需要显式的锁来控制对共享变量的访问。每个线程都有自己的变量副本,因此在多线程环境中,它们不会...

    java-concurrent:java 并发编程例子

    首先,`java.util.concurrent`包中的核心组件包括`Executor`、`Future`、`Callable`、`ThreadLocal`以及各种并发集合如`ConcurrentHashMap`、`CopyOnWriteArrayList`等。这些组件设计的目标是提高多线程环境下的性能...

    concurrent_programming_in_java_design_principles_a_Currency_desi

    "concurrent_programming_in_java_design_principles_a_Currency_desi"这个标题暗示了我们将会探讨的是关于Java中的并发设计原则,特别是与货币相关的设计模式。在这个主题中,我们将深入理解如何在多线程环境中有效...

    基于Java多线程同步的安全性研究.pdf

    文章首先介绍了Java多线程同步的必要性和重要性,然后讨论了Java多线程同步机制的实现方法,包括使用synchronized关键字和Java.util.concurrent.locks包中的Lock对象。接着,文章讨论了Java多线程同步机制中可能出现...

    java_util_concurrent_user_guide并发工具包详解

    Java并发工具包(java.util.concurrent)是Java编程中不可或缺的一部分,它为多线程环境提供了高效、安全且易用的工具。这个包包含了各种类和接口,帮助开发者编写高效的并发程序,避免了直接操作线程所带来的复杂性...

    实现Java高并发隔离 模拟

    1. **线程局部存储(ThreadLocal)**:Java中的`ThreadLocal`类提供了一种线程安全的方式,每个线程都有自己的副本,互不干扰。这样可以避免共享数据导致的并发问题,但需要注意内存泄漏的问题。 2. **锁...

    java源码总结-Java-Concurrent-Programming:java并发编程学习总结源码部分

    本项目“Java-Concurrent-Programming”是一个基于《Java并发编程艺术》这本书的学习总结,通过源码分析来深入理解并发编程的核心概念。 在Java中,线程是并发的基础,`Thread`类提供了创建和管理线程的基本功能。...

    java经典多线程面试题

    - java.util.concurrent包中提供了一些线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,这些集合类能够在多线程环境下提供更安全且效率更高的数据操作。 11. Java中ReentrantLock和synchronized...

    java并发编程书籍

    5. **并发容器**:Java并发包(`java.util.concurrent`)提供了一系列优化的并发容器,如`ConcurrentHashMap`、`CopyOnWriteArrayList`、`BlockingQueue`等,它们在内部实现了线程安全的机制,提高了并发性能。...

Global site tag (gtag.js) - Google Analytics