概念 概念 没用ThreadLocal出现线程安全问题 /** * 没用ThreadLocal 五个线程共享了 num变量 * 此时出现了线程安全问题 */ public class demo1 { private static int num = 0 ; /** * 结果下个线程的值是上一
概念
概念
没用ThreadLocal出现线程安全问题
/*** 没用ThreadLocal 五个线程共享了 num变量
* 此时出现了线程安全问题
*/
public class demo1 {
private static int num = 0;
/**
* 结果下个线程的值是上一个线程改变的值
* 因为 num 是线程共享的
*/
public static void main(String[] args) {
Thread[] threads = new Thread[5]; //定义五个线程
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
num += 5;
System.out.println(Thread.currentThread().getName() + ":" + num);
}, "thread-" + i);
}
for (Thread thread : threads) {
thread.start();
}
}
}
控制台输出
thread-0:5thread-1:10
thread-3:20
thread-2:15
thread-4:25
用ThreadLocal解决上面的线程安全问题
/*** 在demo1上做一个改良,使用ThreadLocal
* 通过 ThreadLocal 封装了一个 Integer 类型的 num 静态成员变量,并且初始值是0
* 每个线程相互独立了,同样是 static 变量,对于不同的线程而言,它没有被共享,
* 而是每个线程各一份,这样也就保证了线程安全。 也就是说,TheadLocal 为每一个线程提供了一个独立的副本!
* 搞清楚 ThreadLocal 的原理之后,有必要总结一下 ThreadLocal 的 API,其实很简单。
*/
public class demo2 {
// private static int num = 0;
static ThreadLocal<Integer> num = new ThreadLocal<Integer>() {
protected Integer initialValue() {
return 0; //初始化0
}
};
/**
* 通过ThreadLocal去拿到值 然后修改完了再设置进去
* 结果:
* 每个线程获得都是副本
* @param args
*/
public static void main(String[] args) {
Thread[] threads = new Thread[5]; //定义五个线程
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
Integer localNum = num.get();//获取ThreadLocal中初始化的值
localNum = localNum + 5;
num.set(localNum); //设置修改以后的值
// num += 5;
System.out.println(Thread.currentThread().getName() + ":" + num.get());
}, "thread-" + i);
}
for (Thread thread : threads) {
thread.start();
}
}
}
控制台显示结果
thread-0:5thread-3:5
thread-2:5
thread-1:5
thread-4:5
说明每个线程的变量都隔离了,这样解决了线程安全问题
ThreadLocal里面的泛型不能是static修饰的变量