java threadlocal 并发_java并发:线程同步机制之ThreadLocal

1.简述ThreadLocal

ThreadLocal实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。ThreadLocal是一个线程级别的局部变量,下面是线程局部变量(ThreadLocal variables)的关键点:

A、当使用ThreadLocal维护变量时,若多个线程访问ThreadLocal实例,ThreadLocal为每个使用该变量的线程提供了一个独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

B、从线程的角度看,目标变量就像是线程的本地变量,这也是类名中Local所要表达的意思。

2.细看ThreadLocal

ThreadLocal类很简单,只有四个方法:

(1)void set(T value),该方法用来设置当前线程中变量的副本

(2)public T get(),该方法是用来获取ThreadLocal在当前线程中保存的变量副本

(3)public void remove(),该方法用来移除当前线程中变量的副本,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束以后,对应线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

(4)protected T initialValue(),该方法是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,ThreadLocal中的缺省实现直接返回一个null。

3.ThreadLocal示例

简单的使用方法如下:

packagecom.test;public classThreadMain {//①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值

private static ThreadLocal seqNum = new ThreadLocal() {publicInteger initialValue() {return 0;

}

};//②获取下一个序列值

public intgetNextNum() {

seqNum.set(seqNum.get()+ 1);returnseqNum.get();

}public static voidmain(String[] args) {

ThreadMain sn= newThreadMain();//③ 3个线程共享sn,各自产生序列号

TestClient t1 = newTestClient(sn);

TestClient t2= newTestClient(sn);

TestClient t3= newTestClient(sn);

t1.start();

t2.start();

t3.start();

}private static class TestClient extendsThread {privateThreadMain sn;publicTestClient(ThreadMain sn) {this.sn =sn;

}public voidrun() {for (int i = 0; i < 3; i++) {//④每个线程打出3个序列值

System.out.println("thread[" +Thread.currentThread().getName()+ "] --> sn[" + sn.getNextNum() + "]");

}

}

}

}

结果如下:

thread[Thread-0] --> sn[1]

thread[Thread-2] --> sn[1]

thread[Thread-1] --> sn[1]

thread[Thread-2] --> sn[2]

thread[Thread-0] --> sn[2]

thread[Thread-2] --> sn[3]

thread[Thread-1] --> sn[2]

thread[Thread-1] --> sn[3]

thread[Thread-0] --> sn[3]

另一个案例

packagecom.csu.thread;classGlobalVarManager {private static ThreadLocal globalVars = new ThreadLocal(){protectedString initialValue() {return "hello";

}

};public static ThreadLocalgetglobalVars() {returnglobalVars;

}

}class ThreadRun implementsRunnable {private ThreadLocalt;privateString str;

ThreadRun(ThreadLocaltemp, String s) {this.t =temp;this.str =s;

}

@Overridepublic voidrun() {

System.out.println(Thread.currentThread()+"改变前:" +t.get());

t.set(str);

System.out.println(Thread.currentThread()+"改变后:" +t.get());

}

}public classThreadLocalTry {public static voidmain(String[] args) {for (int i =1; i < 5; i++) {new Thread(new ThreadRun(GlobalVarManager.getglobalVars(), ""+i)).start();

}

}

}

结果如下:

Thread[Thread-0,5,main]改变前:hello

Thread[Thread-1,5,main]改变前:hello

Thread[Thread-0,5,main]改变后:1Thread[Thread-2,5,main]改变前:hello

Thread[Thread-1,5,main]改变后:2Thread[Thread-2,5,main]改变后:3Thread[Thread-3,5,main]改变前:hello

Thread[Thread-3,5,main]改变后:4

上述案例也可按如下方式来实现:

packagecom.csu.test;classGlobalVarManager {private static ThreadLocal globalVars = new ThreadLocal(){protectedString initialValue() {return "hello";

}

};public staticString getGlobalVars() {returnglobalVars.get();

}public static voidsetGlobalVars(String str) {

globalVars.set(str);

}

}class ThreadRun implementsRunnable {private String str = null;publicThreadRun(String temp) {

str=temp;

}

@Overridepublic voidrun() {

System.out.println(Thread.currentThread()+"改变前:" +GlobalVarManager.getGlobalVars());

GlobalVarManager.setGlobalVars(str);

System.out.println(Thread.currentThread()+"改变后:" +GlobalVarManager.getGlobalVars());

}

}public classThreadLocalTest {public static voidmain(String[] args) {for (int i = 1; i < 5; i++) {new Thread(new ThreadRun("" +i)).start();

}

}

}

结果如下:

Thread[Thread-3,5,main]改变前:hello

Thread[Thread-2,5,main]改变前:hello

Thread[Thread-1,5,main]改变前:hello

Thread[Thread-0,5,main]改变前:hello

Thread[Thread-1,5,main]改变后:2Thread[Thread-2,5,main]改变后:3Thread[Thread-3,5,main]改变后:4Thread[Thread-0,5,main]改变后:1

4.ThreadLocal的实现机制

此部分内容暂没有深入研究,欲了解更多内容请参考https://www.cnblogs.com/dennyzhangdd/p/7978455.html

(1)get()方法源码如下:

ed2e2cebf2ad72809d92345a545fc3e2.png

(2)set()方法源码如下:

77706a23563073915d605c43c828cf5b.png

(3)remove()方法源码如下:

7fef6929c8f04abda18c9179fa007514.png

(4)上述几个函数涉及到如下两个函数

bdc61441fe4a0ab308fd20a1a69df761.png

从前述源码可以看出,ThreadLocal的get、set、remove方法都是操作当前线程,而从Thread的源码可以看出该类有一个ThreadLocal.ThreadLocalMap类型的变量threadLocals,该变量在初次调用ThreadLocal的set()方法时通过createMap()方法初始化

5.ThreadLocalMap

ThreadLocalMap的部分源码如下:

/*** ThreadLocalMap is a customized hash map suitable only for

* maintaining thread local values. No operations are exported

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

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

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

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

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

* the table starts running out of space.*/

static classThreadLocalMap {/*** 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>{/**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.*/

privateEntry[] 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

/*** Set the resize threshold to maintain at worst a 2/3 load factor.*/

private void setThreshold(intlen) {

threshold= len * 2 / 3;

}/*** Increment i modulo len.*/

private static int nextIndex(int i, intlen) {return ((i + 1 < len) ? i + 1 : 0);

}/*** Decrement i modulo len.*/

private static int prevIndex(int i, intlen) {return ((i - 1 >= 0) ? i - 1 : len - 1);

}/*** 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= newEntry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

table[i]= newEntry(firstKey, firstValue);

size= 1;

setThreshold(INITIAL_CAPACITY);

}/*** Construct a new map including all Inheritable ThreadLocals

* from given parent map. Called only by createInheritedMap.

*

*@paramparentMap the map associated with parent thread.*/

privateThreadLocalMap(ThreadLocalMap parentMap) {

Entry[] parentTable=parentMap.table;int len =parentTable.length;

setThreshold(len);

table= newEntry[len];for (int j = 0; j < len; j++) {

Entry e=parentTable[j];if (e != null) {

@SuppressWarnings("unchecked")

ThreadLocal key = (ThreadLocal) e.get();if (key != null) {

Object value=key.childValue(e.value);

Entry c= newEntry(key, value);int h = key.threadLocalHashCode & (len - 1);while (table[h] != null)

h=nextIndex(h, len);

table[h]=c;

size++;

}

}

}

}

此处重点关注一下ThreadLocalMap中的几个成员变量及方法

(1)private Entry[] table;

table是一个Entry类型的数组,该变量在ThreadLocalMap的构造函数中初始化

Entry是ThreadLocalMap的一个内部类

(2)set()方法

/*** Set the value associated with key.

*

*@paramkey the thread local object

*@paramvalue the value to be set*/

private void set(ThreadLocal>key, Object value) {//We don't use a fast path as with get() because it is at//least as common to use set() to create new entries as//it is to replace existing ones, in which case, a fast//path would fail more often than not.

Entry[] tab=table;int len =tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e =tab[i];

e!= null;

e= tab[i =nextIndex(i, len)]) {

ThreadLocal> k =e.get();if (k ==key) {

e.value=value;return;

}if (k == null) {

replaceStaleEntry(key, value, i);return;

}

}

tab[i]= newEntry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >=threshold)

rehash();

}

(3)getEntry()方法

/*** 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.

*

*@paramkey the thread local object

*@returnthe entry associated with key, or null if no such*/

private Entry getEntry(ThreadLocal>key) {int i = key.threadLocalHashCode & (table.length - 1);

Entry e=table[i];if (e != null && e.get() ==key)returne;else

returngetEntryAfterMiss(key, i, e);

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

* its direct hash slot.

*

*@paramkey the thread local object

*@parami the table index for key's hash code

*@parame the entry at table[i]

*@returnthe entry associated with key, or null if no such*/

private Entry getEntryAfterMiss(ThreadLocal> key, inti, Entry e) {

Entry[] tab=table;int len =tab.length;while (e != null) {

ThreadLocal> k =e.get();if (k ==key)returne;if (k == null)

expungeStaleEntry(i);elsei=nextIndex(i, len);

e=tab[i];

}return null;

}

(4)remove()方法

/*** Remove the entry for key.*/

private void remove(ThreadLocal>key) {

Entry[] tab=table;int len =tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e =tab[i];

e!= null;

e= tab[i =nextIndex(i, len)]) {if (e.get() ==key) {

e.clear();

expungeStaleEntry(i);return;

}

}

}

6.总结

ThreadLocal一般都是声明在静态变量中,如果不断地创建ThreadLocal而没有调用其remove方法,将导致内存泄露,特别是在高并发的Web容器当中。

ThreadLocal在处理线程的局部变量时比synchronized同步机制解决线程安全问题更简单,更方便,且程序拥有更高的并发性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值