目录
1.1示例:不使用ThreadLocal,使用static,每个值修改同一个变量,产生错误
四、ThreadLocal在线程中的使用,即怎么让多个线程使用threadLocal
一、why:为什么要用ThreadLocal
1.1示例:不使用ThreadLocal,使用static,每个值修改同一个变量,产生错误
/**
* 不使用ThreadLocal共同使用变量,使用static,每个值修改同一个变量,产生错误
* @author: honry.guan
* @create: 2020-06-07 18:53
**/
public class NoThreadLocalTest extends Thread{
private static int num = 1;
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num);
num = num + 1;
System.out.println(Thread.currentThread().getName()+" 结束,num = "+num);
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
NoThreadLocalTest t = new NoThreadLocalTest();
t.start();
}
}
}
1.2运行结果,可能有几种可能性:
Thread-1: 开始执行,num = 1
Thread-0: 开始执行,num = 1
Thread-0 结束,num = 3
Thread-2: 开始执行,num = 1
Thread-1 结束,num = 2
Thread-2 结束,num = 4
也有可能是:
Thread-1: 开始执行,num = 1
Thread-2: 开始执行,num = 1
Thread-2 结束,num = 2
Thread-0: 开始执行,num = 1
Thread-0 结束,num = 4
Thread-1 结束,num = 4
1.3使用ThreadLocal
public class NoThreadLocalTest extends Thread{
private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
NoThreadLocalTest t = new NoThreadLocalTest();
t.start();
}
}
}
1.4结果:都是2
Thread-0: 开始执行,num = 1
Thread-2: 开始执行,num = 1
Thread-2 结束,num = 2
Thread-1: 开始执行,num = 1
Thread-0 结束,num = 2
Thread-1 结束,num = 2
二、ThreadLocal的使用
2.1ThreadLocal类接口很简单,常用的有4个方法
• void set(Object value):设置当前线程的线程局部变量的值。
• public Object get():该方法返回当前线程所对应的线程局部变量。
• public void remove():移除变量,如果最开始是1,通过set修改之后,调用remove之后,get出来的值又变成1
• protected Object initialValue():初始,第一次调用set或get才执行,并且仅执行一次。
2.2remove的测试
public class NoThreadLocalTest extends Thread{
private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
num.remove();
System.out.println(Thread.currentThread().getName()+" 结束remove之后,num = "+num.get());
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
NoThreadLocalTest t = new NoThreadLocalTest();
t.start();
}
}
}
2.3执行结果
Thread-1: 开始执行,num = 1
Thread-2: 开始执行,num = 1
Thread-0: 开始执行,num = 1
Thread-0 结束,num = 2
Thread-2 结束,num = 2
Thread-1 结束,num = 2
Thread-1 结束remove之后,num = 1
Thread-2 结束remove之后,num = 1
Thread-0 结束remove之后,num = 1
三、ThreadLocal初始化方法的使用
1、实现方法initialValue
private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
2、通过lomoba表达式
ThreadLocal<Integer> num = ThreadLocal.withInitial(()->1);
四、ThreadLocal在线程中的使用,即怎么让多个线程使用threadLocal
(我刚学的时候一直被这个事情困扰,自己手写几次之后明白)
只要在线程run方法中,调用ThreadLocal的get或者set,不管是线程对象内部自定义threadLocal,还是通过构造函数传递进线程的ThreadLocal变量。
1、线程对象内部自定义threadLocal
public class NoThreadLocalTest extends Thread{
private ThreadLocal<Integer> num = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 1;
}
};
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
num.remove();
System.out.println(Thread.currentThread().getName()+" 结束remove之后,num = "+num.get());
}
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
NoThreadLocalTest t = new NoThreadLocalTest();
t.start();
}
}
}
2、通过构造函数传递进线程的ThreadLocal变量
public class NoThreadLocalTest extends Thread{
private ThreadLocal<Integer> num ;
public NoThreadLocalTest(ThreadLocal<Integer> num) {
this.num = num;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": 开始执行,num = "+num.get());
num.set(num.get() + 1);
System.out.println(Thread.currentThread().getName()+" 结束,num = "+num.get());
num.remove();
System.out.println(Thread.currentThread().getName()+" 结束remove之后,num = "+num.get());
}
public static void main(String[] args) {
ThreadLocal<Integer> num = ThreadLocal.withInitial(()->1);
}
}
此时不论什么一个线程能够并发访问这个变量,对它进行写入、读取操作,都是线程安全的。