ThreadLocal和Synchonized作为线程安全的两种选择,在项目中会大量使用,不过现在一想到线程安全,首先想到的就是方法或者变量中用Synchonized修饰,用锁的形式,保证多线程情况下的安全问题。
ThreadLocal在项目中很少使用,但是在面试八股文中这个关键字特别受到面试官老爷的喜欢,里面涉及到的关于弱引用、对象备份、多线程安全概念都是十分重要的。
ThreadLocal,就是线程的本地变量,如果创建了一个ThreadLocal,那么访问这个变量的每个线程都会本地拷贝一份,这个线程的方法都会共享这个变量,其他的线程进来的话,同样会从方法区中再次备份,这样就避免了线程安全。
public class ThreadTest {
//两个不同的线程 会自动复制不同的副本
private static ThreadLocal<String> local = new ThreadLocal<String>();
private static String commonLocal = "C";
static void print(String commonLocalCC) {
//打印当前线程中本地内存中变量的值
System.out.println("ThreadLocal:"+ local.get());
System.out.println("静态块Stirng:"+commonLocal);
//清除内存中的本地变量
local.remove();
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
local.set("AAAAA");
commonLocal = "A";
print(commonLocal);
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
local.set("BBBBB");
commonLocal = "B";
print(commonLocal);
}
},"B").start();
}
}
这段代码,拿普通的静态块和ThreadLocal进行比较会发现,普通的静态块线程A调用print的时候,输出的并不是线程A中赋值的A数值,而是线程B中赋值的B,这个就是线程不安全,线程A和线程B在运行的时候同时操作static修饰的变量commonLocal,当线程A的输出时,commonLocal数值已经变成了线程B修改的值。
这种在项目中会有很大的问题,会导致数据错乱。这就好比,拿支付来说,就好比两个用户同时扣款,用户A余额100,用户B余额500,同样购买一个价值200的商品,可能会导致用户A的余额变成500,从而扣款成功。
这种情况就应该引用线程安全的概念,每个用户进来的金额和其他信息,都是私有化的。不能互相污染。