转自:https://www.cnblogs.com/renyuanwei/p/9635235.html
什么是ThreadLocal
根据JDK文档中的解释:ThreadLocal的作用是提供线程内的局部变量,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。
从这里可以看出,引入ThreadLocal的初衷是为了提供线程内的局部变量
ThreadLocal
不是一个线程,而是一个线程的本地化对象。当某个变量在使用 ThreadLocal
进行维护时,ThreadLocal
为使用该变量的每个线程分配了一个独立的变量副本。
每个线程可以自行操作自己对应的变量副本,而不会影响其他线程的变量副本。
API 方法
ThreadLocal
的 API 提供了如下的 4 个方法。
1)protected T initialValue()
返回当前线程的局部变量副本的变量初始值。
2)T get()
返回当前线程的局部变量副本的变量值,如果此变量副本不存在,则通过 initialValue()
方法创建此副本并返回初始值。
3)void set(T value)
设置当前线程的局部变量副本的变量值为指定值。
4)void remove()
删除当前线程的局部变量副本的变量值。
在实际使用中,我们一般都要重写 initialValue()
方法,设置一个特定的初始值。
关于initialValue的初始化。本人尝试了多种方式:
1
2
3
4
5
6
7
8
|
//new ThreadLocal方式:不推荐
final
ThreadLocal<String> commandThreads =
new
ThreadLocal<String>() {
@Override
protected
String initialValue() {
return
"execute :"
+System.currentTimeMillis();
}
};
System.out.println(commandThreads.get());
|
1
2
3
4
5
|
//withInitial方式:
ThreadLocal<String> commandThreadnew =
// ThreadLocal.withInitial(()-> "execute :"+System.currentTimeMillis());
ThreadLocal.withInitial(()->
new
String(
"execute :"
+System.currentTimeMillis()));
System.out.println(commandThreadnew.get());
|
1
2
3
4
5
6
7
8
9
10
|
//(new Supplier<String>(){}方式 推荐
ThreadLocal<String> commandThreadnew1 =
ThreadLocal.withInitial(
new
Supplier<String>() {
@Override
public
String get() {
return
"execute :"
+System.currentTimeMillis();
}
});
System.out.println( commandThreadnew1.get());
|
以下是关于ThreadLocal 解决多线程变量共享问题:
存在争议点:
ThreadLocal到底能不能解决共享对象的多线程访问问题?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package
com.wuxianjiezh.demo.threadpool;
public
class
MainTest {
public
static
void
main(String[] args) {
Bank bank =
new
Bank();
Thread xMThread =
new
Thread(() -> bank.deposit(
200
),
"小明"
);
Thread xGThread =
new
Thread(() -> bank.deposit(
200
),
"小刚"
);
Thread xHThread =
new
Thread(() -> bank.deposit(
200
),
"小红"
);
xMThread.start();
xGThread.start();
xHThread.start();
}
}
class
Bank {
private
int
money =
1000
;
public
void
deposit(
int
money) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName +
"--当前账户余额为:"
+
this
.money);
this
.money += money;
System.out.println(threadName +
"--存入 "
+ money +
" 后账户余额为:"
+
this
.money);
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
|
运行结果:存在多线程输出结果混乱
1
2
3
4
5
6
|
小明--当前账户余额为:
1000
小红--当前账户余额为:
1000
小红--存入
200
后账户余额为:
1400
小刚--当前账户余额为:
1000
小刚--存入
200
后账户余额为:
1600
小明--存入
200
后账户余额为:
1200
|
使用 ThreadLocal
保存对象的局部变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
public
class
MainTest {
public
static
void
main(String[] args) {
Bank bank =
new
Bank();
Thread xMThread =
new
Thread(() -> bank.deposit(
200
),
"小明"
);
Thread xGThread =
new
Thread(() -> bank.deposit(
200
),
"小刚"
);
Thread xHThread =
new
Thread(() -> bank.deposit(
200
),
"小红"
);
xMThread.start();
xGThread.start();
xHThread.start();
}
}
class
Bank {
// 初始化账户余额为 100
ThreadLocal<Integer> account = ThreadLocal.withInitial(
new
Supplier<Integer>() {
@Override
public
Integer get() {
return
1000
;
}
});
public
void
deposit(
int
money) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName +
"--当前账户余额为:"
+ account.get());
account.set(account.get() + money);
System.out.println(threadName +
"--存入 "
+ money +
" 后账户余额为:"
+ account.get());
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
}
|
运行结果为:
1
2
3
4
5
6
7
|
小明--当前账户余额为:
1000
小红--当前账户余额为:
1000
小红--存入
200
后账户余额为:
1200
小刚--当前账户余额为:
1000
小刚--存入
200
后账户余额为:
1200
小明--存入
200
后账户余额为:
1200
可以看到,我们要的效果达到了。各线程间同时操作自己的变量,相互间没有影响。
|
ThreadLocal 与 Thread 同步机制的比较
-
同步机制采用了以时间换空间方式,通过对象锁保证在同一个时间,对于同一个实例对象,只有一个线程访问。
-
ThreadLocal
采用以空间换时间方式,为每一个线程都提供一份变量,各线程间同时访问互不影响。