之前的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,无需同步,大大提升了性能。
分享到:
相关推荐
应优先考虑其他同步机制,如`synchronized`关键字或`java.util.concurrent`包中的工具类。 2. **及时清理**:确保在不再使用ThreadLocal时调用`remove()`,以防止内存泄漏。 3. **谨慎使用在长生命周期线程中**:如...
Java并发包(java.concurrent)是Java平台中处理多线程编程的核心工具包,它提供了丰富的类和接口,使得开发者能够高效、安全地编写多线程程序。这个包的设计目标是提高并发性能,减少同步代码的复杂性,并提供高级...
《Java Util Concurrent中文版》是Java并发编程领域的重要参考资料,主要涵盖了Java标准库中的`java.util.concurrent`包及其相关类和接口。这个包是Java多线程编程的核心,提供了高效、安全的并发工具,帮助开发者...
import java.util.concurrent.atomic.AtomicInteger; public class UniqueThreadIdGenerator { private static final AtomicInteger uniqueId = new AtomicInteger(0); private static final 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.util.concurrent.atomic`包)。 3. **避免死锁、活锁和饥饿**:理解并预防这些并发问题至关重要。死锁发生在两个或多个线程相互等待对方释放资源导致僵局;活锁是线程不断...
Java的`concurrent`包是Java多线程编程的核心组件,它包含了一系列高效、线程安全的类和接口,使得开发者能够更容易地处理并发问题。这个包中的工具和类是Java平台对并行编程的强大支持,它极大地提升了程序在多...
在Java编程语言中,"concurrent"一词通常与多线程并发编程相关,它涉及到如何在多个执行线程之间高效、安全地共享资源。这里的"concurrent源代码"可能是指Java并发包(`java.util.concurrent`)中的源代码,这个包包含...
这里提到的`concurrent-1.3.4-sources.jar`是一个特定版本的Java并发库源代码包,它包含了Java并发编程的核心组件和工具,对理解并发编程有着重要的参考价值。这个版本是1.3.4,虽然相比现代的Java版本可能有些过时...
Java 5并发包(`java.util.concurrent`,简称`Concurrent`包)是Java平台中用于多线程编程的重要组成部分,它提供了丰富的并发工具类,极大地简化了在多线程环境下的编程工作。这个包的设计目标是提高并发性能,减少...
与传统的同步机制(如`synchronized`关键字或`java.util.concurrent`包中的工具)相比,ThreadLocal的优势在于它不需要显式的锁来控制对共享变量的访问。每个线程都有自己的变量副本,因此在多线程环境中,它们不会...
首先,`java.util.concurrent`包中的核心组件包括`Executor`、`Future`、`Callable`、`ThreadLocal`以及各种并发集合如`ConcurrentHashMap`、`CopyOnWriteArrayList`等。这些组件设计的目标是提高多线程环境下的性能...
"concurrent_programming_in_java_design_principles_a_Currency_desi"这个标题暗示了我们将会探讨的是关于Java中的并发设计原则,特别是与货币相关的设计模式。在这个主题中,我们将深入理解如何在多线程环境中有效...
文章首先介绍了Java多线程同步的必要性和重要性,然后讨论了Java多线程同步机制的实现方法,包括使用synchronized关键字和Java.util.concurrent.locks包中的Lock对象。接着,文章讨论了Java多线程同步机制中可能出现...
Java并发工具包(java.util.concurrent)是Java编程中不可或缺的一部分,它为多线程环境提供了高效、安全且易用的工具。这个包包含了各种类和接口,帮助开发者编写高效的并发程序,避免了直接操作线程所带来的复杂性...
1. **线程局部存储(ThreadLocal)**:Java中的`ThreadLocal`类提供了一种线程安全的方式,每个线程都有自己的副本,互不干扰。这样可以避免共享数据导致的并发问题,但需要注意内存泄漏的问题。 2. **锁...
本项目“Java-Concurrent-Programming”是一个基于《Java并发编程艺术》这本书的学习总结,通过源码分析来深入理解并发编程的核心概念。 在Java中,线程是并发的基础,`Thread`类提供了创建和管理线程的基本功能。...
- java.util.concurrent包中提供了一些线程安全的集合类,如ConcurrentHashMap、CopyOnWriteArrayList等,这些集合类能够在多线程环境下提供更安全且效率更高的数据操作。 11. Java中ReentrantLock和synchronized...
5. **并发容器**:Java并发包(`java.util.concurrent`)提供了一系列优化的并发容器,如`ConcurrentHashMap`、`CopyOnWriteArrayList`、`BlockingQueue`等,它们在内部实现了线程安全的机制,提高了并发性能。...