Spring中的线程安全性

  • 时间:
  • 浏览:1
  • 来源:UU快3—全天最准大发快三

* Returns the value in the current thread's copy of this

ThreadLocal中只蕴含俩个 多成员变量,你什儿 个多变量后要与ThreadLocalMap的hash策略相关的。

*/

* by an invocation of the {@link #initialValue} method.

if (m != null)

唯一的实例变量threadLocalHashCode是用来进行寻址的hashcode,它由函数nextHashCode()生成,该函数简单地通过俩个 多增量HASH_INCREMENT来生成hashcode。至于为哪几个你什儿 增量为0x61c88647,主不要 可能性ThreadLocalMap的初始大小为16,每次扩容后要为俩个 多多的2倍,俩个 多多它的容量永远为2的n次方,该增量选为0x61c88647也是为了尽可能性均匀地分布,减少碰撞冲突。

* inheritableThreadLocals). The ThreadLocal objects act as keys,

private final int threadLocalHashCode = nextHashCode();

* ThreadLocalMap is a customized hash map suitable only for

* (useful only within ThreadLocalMaps) that eliminates collisions

ThreadLocalMap.Entry e = map.getEntry(this);

static class Entry extends WeakReference<ThreadLocal<?>> {

* unless its value is {@linkplain #set set} by the current thread

}

 ●  prototype:bean被定义为在每次注入时后要创建俩个 多新的对象。 ●  request:bean被定义为在每个HTTP请求中创建俩个 多单例对象,却说要 说在单个请求中后要复用你什儿 个多单例对象。 ●  session:bean被定义为在俩个 多session的生命周期内创建俩个 多单例对象。 ●  application:bean被定义为在ServletContext的生命周期中复用俩个 多单例对象。 ●  websocket:bean被定义为在websocket的生命周期中复用俩个 多单例对象。

亲戚朋友 交由Spring管理的大多数对象确实后要其他无请况的对象,你什儿 无需可能性多守护程序而导致 请况被破坏的对象很适合Spring的默认scope,每个单例的无请况对象后要守护程序安全的(也还都还里能说只不要 无请况的对象,不管单例多例后要守护程序安全的,不过单例毕竟节省了不断创建对象与GC的开销)

*/

/**

* override this method, relying solely on the {@link #initialValue}

* InheritableThreadLocal values pertaining to this thread. This map is

if (map != null)

* @param firstValue value for the initial entry of the map

* zero.

* Get the map associated with a ThreadLocal. Overridden in

* The initial capacity -- MUST be a power of two.

* Sets the current thread's copy of this thread-local variable

/**

return e;

*

* @return the current thread's value of this thread-local

*/

setInitialValue()函数会去先调用initialValue()函数来生成初始值,该函数默认返回null,亲戚朋友 还都还里能通过重写你什儿 函数来返回亲戚朋友 让你在ThreadLocal中维护的变量。却说 ,去调用getMap()函数获得ThreadLocalMap,可能性该map可能性地处,不都还里能就用新获得value去覆盖旧值,否则就调用createMap()函数来创建新的map。

* in the interim. This may result in multiple invocations of the

int i = key.threadLocalHashCode & (table.length - 1);

return getEntryAfterMiss(key, i, e);

* by making this method readily inlinable.

}

// Thread中的源码

仔细想想确实就也能理解你什儿 设计的思想。有五种普遍的土办法是通过俩个 多全局的守护程序安全的Map来存储各个守护程序的变量副本,否则你什儿 做法可能性完整版违背了ThreadLocal的本意,设计ThreadLocal的初衷不要 为了处里多个守护程序去并发访问同俩个 多对象,尽管它是守护程序安全的。而在每个Thread中存放与它关联的ThreadLocalMap是完整版符合ThreadLocal的思想的,当让你对守护程序局部变量进行操作时,只都还里能把Thread作为key来获得Thread中的ThreadLocalMap即可。你什儿 设计相比采用俩个 多全局Map的土办法会多占用不要 内存空间,但全是却说不都还里能额外的采取锁等守护程序同步土办法而节省了时间上的消耗。

....

* used, stale entries are guaranteed to be removed only when

* designed to maximize performance for direct hits, in part

* @param i the table index for key's hash code

if (k == null)

/**

while (e != null) {

* to each thread (Thread.threadLocals and

map.set(this, value);

*

void createMap(Thread t, T firstValue) {

ThreadLocal的set()与remove()函数要比get()的实现都还里能简单,都不要 通过getMap()来获得ThreadLocalMap否则对其进行操作。

private Entry getEntry(ThreadLocal<?> key) {

ThreadLocal与像synchronized俩个 多多的锁机制是不同的。首先,它们的应用场景与实现思路就不一样,锁更强调的是何如同步多个守护程序去正确地共享俩个 多变量,ThreadLocal则是为了处里同俩个 多变量何如不被多个守护程序共享。从性能开销的淬硬层 上来讲,可能性锁机制是用时间换空间语录,不都还里能ThreadLocal不要 用空间换时间。

else

}

return result;

* @param t the current thread

* @return the map

}

*/

/**

}

return value;

* the table starts running out of space.

else

* ThreadLocalMaps are constructed lazily, so we only create

无请况的对象即是自身不都还里能请况的对象,自然也就无需可能性多个守护程序的交替调度而破坏自身请况导致 守护程序安全难题。无请况对象包括亲戚朋友 总爱使用的DO、DTO、VO哪几个只作为数据的实体模型的贫血对象,还有Service、DAO和Controller,哪几个对象并不都还里能自己的请况,它们不要 用来执行其他操作的。累似 ,每个DAO提供的函数都不要 对数据库的CRUD,否则每个数据库Connection都作为函数的局部变量(局部变量是在用户栈中的,否则用户栈五种不要 守护程序私有的内存区域,不要 不地处守护程序安全难题),用完即关(或交还给连接池)。

int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

/**

* Construct a new map initially containing (firstKey, firstValue).

/**

* key. It otherwise relays to getEntryAfterMiss. This is

* in the common case where consecutively constructed ThreadLocals

T result = (T)e.value;

/** The value associated with this ThreadLocal. */

* maintaining thread local values. No operations are exported

* WeakReferences for keys. However, since reference queues are not

public void remove() {

private static final int INITIAL_CAPACITY = 16;

*/

ThreadLocal<?> k = e.get();

* @return the entry associated with key, or null if no such

* implicit sequential thread-local IDs into near-optimally spread

return null;

/**

else

public void set(T value) {

/**

* @param key the thread local object

e = tab[i];

T value = initialValue();

Entry e = table[i];

/**

private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {

*/

* @return the entry associated with key, or null if no such

}

new AtomicInteger();

二:ThreadLocal

* InheritableThreadLocal.

* reinitialized by invoking its {@link #initialValue} method,

* outside of the ThreadLocal class. The class is package private to

* @return the initial value

* Get the entry associated with key. This method

createMap(t, value);

*/

return t.threadLocals;

*

ThreadLocalMap getMap(Thread t) {

* The difference between successively generated hash codes - turns

}

下面将通过解析ThreadLocal的源码来了解它的实现与作用,ThreadLocal是俩个 多很好用的工具类,它在其他请况下处里了守护程序安全难题(在变量不都还里能被多个守护程序共享时)。

ThreadLocal是俩个 多为守护程序提供守护程序局部变量的工具类。它的思想也十分简单,不要 为守护程序提供俩个 多守护程序私有的变量副本,俩个 多多多个守护程序都还都还里能随意更改自己守护程序局部的变量,无需影响到其他守护程序。不过都还里能注意的是,ThreadLocal提供的不要 俩个 多浅拷贝,可能性变量是俩个 多引用类型,不都还里能就要考虑它结构的请况算不算会被改变,让你处里你什儿 难题还都还里能通过重写ThreadLocal的initialValue()函数来自己实现深拷贝,建议在使用ThreadLocal时一却说 现在开始就重写该函数。

*/

* its direct hash slot.

return nextHashCode.getAndAdd(HASH_INCREMENT);

*/

* @param t the current thread

ThreadLocalMap map = getMap(t);

}

if (e != null) {

* by the ThreadLocal class. */

* are used by the same threads, while remaining well-behaved in

size = 1;

* allow declaration of fields in class Thread. To help deal with

getMap()函数与createMap()函数的实现也十分简单,否则通过观察你什儿 个多函数还都还里能发现俩个 多秘密:ThreadLocalMap是存上放Thread中的。

*/

* very large and long-lived usages, the hash table entries use

if (map != null) {

map.set(this, value);

}

ThreadLocalMap m = getMap(Thread.currentThread());

/**

* ThreadLocal object). Note that null keys (i.e. entry.get()

ThreadLocal.ThreadLocalMap threadLocals = null;

Entry[] tab = table;

* searched via threadLocalHashCode. This is a custom hash code

ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

table[i] = new Entry(firstKey, firstValue);

/**

* Returns the next hash code.

i = nextIndex(i, len);

m.remove(this);

private static int nextHashCode() {

/**

* @param value the value to be stored in the current thread's copy of

}

if (k == key)

* Removes the current thread's value for this thread-local

* as "stale entries" in the code that follows.

在使用守护程序池的请况下,可能性不及时进行清理,内存泄漏难题事小,甚至后要产生守护程序逻辑上的难题。不要 ,为了安全地使用ThreadLocal,都还里能要像每次使用完锁就解锁一样,在每次使用完ThreadLocal却说要调用remove()来清理无用的Entry。

}

 ●  弱引用key:ThreadLocal被设置为null,可能性ThreadLocalMap持有ThreadLocal的弱引用,即便不手动删除,ThreadLocal仍会被回收,ThreadLocalMap在却说 调用set()、getEntry()和remove()函数后要清除所有key为null的Entry。

但要注意的是,ThreadLocalMap仅仅蕴含哪几个被动土办法来处里内存泄漏难题。可能性你在却说 不都还里能调用ThreadLocalMap的set()、getEntry()和remove()函数语录,不都还里能仍然会地处内存泄漏难题。

* Variant of set() to establish initialValue. Used instead

else

* {@code initialValue} method in the current thread.

* this thread-local.

* its main ref field as the key (which is always a

/*

亲戚朋友 要考虑五种会地处内存泄漏的请况,可能性ThreadLocal被设置为null后,否则不都还里能任何强引用指向它,根据垃圾回收的可达性分析算法,ThreadLocal可能性被回收。俩个 多多一来,ThreadLocalMap中就会蕴含key为null的Entry,否则ThreadLocalMap是在Thread中的,假如有一天守护程序迟迟不却说 现在开始,哪几个无法访问到的value会形成内存泄漏。为了处里你什儿 难题,ThreadLocalMap中的getEntry()、set()和remove()函数后要清理key为null的Entry,以下面的getEntry()函数的源码为例。

* InheritableThreadLocal.

在上文中亲戚朋友 发现了ThreadLocalMap的key是俩个 多弱引用,不都还里能为哪几个使用弱引用呢?使用强引用key与弱引用key的差别如下:

ThreadLocal蕴含有俩个 多叫做ThreadLocalMap的结构类,该类为俩个 多采用线性探测法实现的HashMap。它的key为ThreadLocal对象否则还使用了WeakReference,ThreadLocalMap正是用来存储变量副本的。

/**

Thread t = Thread.currentThread();

*/

*

* itself handles only the fast path: a direct hit of existing

super(k);

}

Spring作为俩个 多IOC/DI容器,帮助亲戚朋友 管理了许其他多的“bean”。但确实,Spring并不都还里能保证哪几个对象的守护程序安全,都还里能由开发者自己编写处里守护程序安全难题的代码。

*/

createMap(t, value);

* The entries in this hash map extend WeakReference, using

}

*/

* to the specified value. Most subclasses will have no need to

Thread t = Thread.currentThread();

ThreadLocalMap map = getMap(t);

通过阅读上文确实可能性说的很清楚了,Spring根本就不都还里能对bean的多守护程序安全难题做出任何保证与土办法。对于每个bean的守护程序安全难题,根本导致 是每个bean自身的设计。暂且在bean中声明任何有请况的实例变量或类变量,可能性都还里能不都还里能,不都还里能就使用ThreadLocal把变量变为守护程序私有的,可能性bean的实例变量或类变量都还里能在多个守护程序之间共享,不都还里能就不都还里能使用synchronized、lock、CAS等哪几个实现守护程序同步的土办法了。

ThreadLocalMap map = getMap(t);

*/

@SuppressWarnings("unchecked")

if (e != null && e.get() == key)

* @since 1.5

table = new Entry[INITIAL_CAPACITY];

value = v;

* @param key the thread local object

*

return null;

/**

Spring对每个bean提供了俩个 多scope属性来表示该bean的作用域。它是bean的生命周期。累似 ,俩个 多scope为singleton的bean,在第一次被注入时,会创建为俩个 多单例对象,该对象会总爱被复用到应用却说 现在开始。

* @param e the entry at table[i]

/**

setThreshold(INITIAL_CAPACITY);

private T setInitialValue() {

}

// 清理key为null的Entry

* less common cases.

* ThreadLocals rely on per-thread linear-probe hash maps attached

}

* Create the map associated with a ThreadLocal. Overridden in

*/

* The next hash code to be given out. Updated atomically. Starts at

int len = tab.length;

Entry(ThreadLocal<?> k, Object v) {

* method to set the values of thread-locals.

* variable. If this thread-local variable is subsequently

/* ThreadLocal values pertaining to this thread. This map is maintained

return setInitialValue();

public T get() {

* entry can be expunged from table. Such entries are referred to

* thread-local variable. If the variable has no value for the

expungeStaleEntry(i);

*/

* current thread, it is first initialized to the value returned

Thread t = Thread.currentThread();

* of set() in case user has overridden the set() method.

人们可能性会认为,我使用request作用域不就还都还里能处里每个请求之间的安全难题多会儿?这是完整版错误的,可能性Controller默认是单例的,俩个 多controller对象是会被多个守护程序共享的,这就又回到了守护程序的安全难题。当然,你也还都还里能把Controller的scope改成prototype,实际上Struts2不要 不都还里能做的,但有其他要注意,Spring MVC对请求的拦截粒度是基于每个土办法的,而Struts2是基于每个类的,不要 把Controller设为多例可能性频繁的创建与回收对象,严重影响到了性能。

三:ThreadLocal的内存泄漏

要获得当前守护程序私有的变量副本都还里能调用get()函数。首先,它会调用getMap()函数去获得当前守护程序的ThreadLocalMap,你什儿 函数都还里能接收当前守护程序的实例作为参数。可能性得到的ThreadLocalMap为null,不都还里能就去调用setInitialValue()函数来进行初始化,可能性不为null,就通过map来获得变量副本并返回。

*

protected T initialValue() {

return e;

/**

private static AtomicInteger nextHashCode =

* one when we have at least one entry to put in it.

}

*/

}

* Version of getEntry method for use when key is not found in

* {@linkplain #get read} by the current thread, its value will be

*

* == null) mean that the key is no longer referenced, so the

if (map != null)

*

private static final int HASH_INCREMENT = 0x61c88647;

Object value;

* multiplicative hash values for power-of-two-sized tables.

t.threadLocals = new ThreadLocalMap(this, firstValue);

static class ThreadLocalMap {

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {

* maintained by the InheritableThreadLocal class.