java

位置:IT落伍者 >> java >> 浏览文章

Java线程:深入ThreadLocal


发布日期:2023年02月15日
 
Java线程:深入ThreadLocal

ThreadLocal与线程成员变量还有区别ThreadLocal该类提供了线程局部变量这个局部变量与一般的成员变量不一样ThreadLocal的变量在被多个线程使用时候每个线程只能拿到该变量的一个副本这是Java API中的描述通过阅读API源码发现并非副本副本什么概念?克隆品? 或者是别的样子太模糊

准确的说应该是ThreadLocal类型的变量内部的注册表(Map<ThreadT>)发生了变化但ThreadLocal类型的变量本身的确是一个这才是本质!

下面就做个例子

标准例子

定义了MyThreadLocal类创建它的一个对象tlt分别给四个线程使用结果四个线程tlt变量并没有出现共用现象二是各用各的这说明四个线程使用的是tlt的副本(克隆品)

/**

* 使用了ThreadLocal的类

*

* @author leizhimin ::

*/

public class MyThreadLocal {

//定义了一个ThreadLocal变量用来保存int或Integer数据

private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {

@Override

protected Integer initialValue() {

return ;

}

};

public Integer getNextNum() {

//将tl的值获取后加并更新设置t的值

tlset(tlget() + );

return tlget();

}

}

/**

* 测试线程

*

* @author leizhimin ::

*/

public class TestThread extends Thread {

private MyThreadLocal tlt = new MyThreadLocal();

public TestThread(MyThreadLocal tlt) {

thistlt = tlt;

}

@Override

public void run() {

for (int i = ; i < ; i++) {

Systemoutprintln(ThreadcurrentThread()getName() + \t + tltgetNextNum());

}

}

}

/**

* ThreadLocal测试

*

* @author leizhimin ::

*/

public class Test {

public static void main(String[] args) {

MyThreadLocal tlt = new MyThreadLocal();

Thread t = new TestThread(tlt);

Thread t = new TestThread(tlt);

Thread t = new TestThread(tlt);

Thread t = new TestThread(tlt);

tstart();

tstart();

tstart();

tstart();

}

}

可以看出三个线程各自独立编号互不影响

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Process finished with exit code

tlt对象是一个废话tl对象也是一个因为组合关系是一对一的但是tl对象内部的Map随着线程的增多会创建很多Integer对象只是Integer和int已经通用了所以感觉不到Integer的对象属性

不用ThreadLocal

假如不用ThreadLocal只需要将MyThreadLocal类重新定义为

/**

* 使用了ThreadLocal的类

*

* @author leizhimin ::

*/

public class MyThreadLocal {

private Integer t = ;

public Integer getNextNum(){

return t=t+;

}

// //定义了一个ThreadLocal变量用来保存int或Integer数据

// private ThreadLocal<Integer> tl = new ThreadLocal<Integer>() {

// @Override

// protected Integer initialValue() {

// return ;

// }

// };

//

// public Integer getNextNum() {

// //将tl的值获取后加并更新设置t的值

// tlset(tlget() + );

// return tlget();

// }

}

然后运行测试

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Process finished with exit code

从这里可以看出四个线程共享了tlt变量结果每个线程都直接修改tlt的属性

自己实现个ThreadLocal

package comlavasofttest;

import javautilCollections;

import javautilHashMap;

import javautilMap;

/**

* 使用了ThreadLocal的类

*

* @author leizhimin ::

*/

public class MyThreadLocal {

//定义了一个ThreadLocal变量用来保存int或Integer数据

private comlavasofttestThreadLocal<Integer> tl = new comlavasofttestThreadLocal<Integer>() {

@Override

protected Integer initialValue() {

return ;

}

};

public Integer getNextNum() {

//将tl的值获取后加并更新设置t的值

tlset(tlget() + );

return tlget();

}

}

class ThreadLocal<T> {

private Map<Thread T> map = CollectionssynchronizedMap(new HashMap<Thread T>());

public ThreadLocal() {

}

protected T initialValue() {

return null;

}

public T get() {

Thread t = ThreadcurrentThread();

T obj = mapget(t);

if (obj == null && !ntainsKey(t)) {

obj = initialValue();

mapput(t obj);

}

return obj;

}

public void set(T value) {

mapput(ThreadcurrentThread() value);

}

public void remove() {

mapremove(ThreadcurrentThread());

}

}

运行测试

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Thread

Process finished with exit code

很意外这个山寨版的ThreadLocal也同样运行很好实现了JavaAPI中ThreadLocal的功能

透过现象看本质

其实从程序角度看tlt变量的确是一个毫无疑问的但是为什么打印出来的数字就互不影响呢?

是因为使用了Integer吗?不是

原因是protected T initialValue()和get()因为每个线程在调用get()时候发现Map中不存在就创建调用它的时候就创建了一个新变量类型为T每次都新建当然各用个的互不影响了

为了看清本质将Integer换掉重写部分类

package comlavasofttest;

import javautilCollections;

import javautilHashMap;

import javautilMap;

/**

* 使用了ThreadLocal的类

*

* @author leizhimin ::

*/

public class MyThreadLocal {

//定义了一个ThreadLocal变量用来保存int或Integer数据

// private ThreadLocal<Bean> tl = new ThreadLocal<Bean>() {

private comlavasofttestThreadLocal<Bean> tl = new comlavasofttestThreadLocal<Bean>() {

@Override

protected Bean initialValue() {

return new Bean();

}

};

@Override

public String toString() {

return MyThreadLocal{ +

tl= + tl +

};

}

public Bean getBean() {

return tlget();

}

}

class ThreadLocal<T> {

private Map<Thread T> map = CollectionssynchronizedMap(new HashMap<Thread T>());

public ThreadLocal() {

}

protected T initialValue() {

return null;

}

public T get() {

Thread t = ThreadcurrentThread();

T obj = mapget(t);

if (obj == null && !ntainsKey(t)) {

obj = initialValue();

mapput(t obj);

}

return obj;

}

public void set(T value) {

mapput(ThreadcurrentThread() value);

}

public void remove() {

mapremove(ThreadcurrentThread());

}

}

package comlavasofttest;

/**

* 测试Bean

*

* @author leizhimin ::

*/

public class Bean {

private String id = ;

private String name = none;

public Bean() {

}

public Bean(String id String name) {

thisid = id;

thisname = name;

}

public String getId() {

return id;

}

public void setId(String id) {

thisid = id;

}

public String getName() {

return name;

}

public void setName(String name) {

thisname = name;

}

public String showinfo() {

return Bean{ +

id= + id + \ +

name= + name + \ +

};

}

}

package comlavasofttest;

/**

* 测试线程

*

* @author leizhimin ::

*/

public class TestThread extends Thread {

private MyThreadLocal tlt = new MyThreadLocal();

public TestThread(MyThreadLocal tlt) {

thistlt = tlt;

}

@Override

public void run() {

Systemoutprintln(>>>>>: + tlt);

for (int i = ; i < ; i++) {

Systemoutprintln(ThreadcurrentThread()getName() + \t +tltgetBean()+\t+tltgetBean()showinfo());

}

}

}

然后运行测试

>>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

>>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

>>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

>>>>>:MyThreadLocal{tl=comlavasofttestMyThreadLocal$@defd}

Thread comlavasofttestBean@aff Bean{id= name=none}

Thread comlavasofttestBean@feb Bean{id= name=none}

Thread comlavasofttestBean@db Bean{id= name=none}

Thread comlavasofttestBean@feb Bean{id= name=none}

Thread comlavasofttestBean@feb Bean{id= name=none}

Thread comlavasofttestBean@aff Bean{id= name=none}

Thread comlavasofttestBean@db Bean{id= name=none}

Thread comlavasofttestBean@db Bean{id= name=none}

Thread comlavasofttestBean@aff Bean{id= name=none}

Thread comlavasofttestBean@aff Bean{id= name=none}

Thread comlavasofttestBean@aff Bean{id= name=none}

Thread comlavasofttestBean@aff Bean{id= name=none}

Process finished with exit code

从打印结果很清楚的看到MyThreadLocal的tlt对象的确是一个tlt对象里的ThreadLocal的tl对象也是一个但是将tt给每个线程用的时候线程会重新创建Bean对象加入到ThreadLocal的Map中去使用

               

上一篇:漫谈Java加密技术(一)

下一篇:Java多线程之ConcurrentHashMap深入分析