最近看到面试题:String类和StringBuffer、StringBuilder的区别里有一条是说:String类不可变,所以是线程安全的。之前从来没有认真思考过这个答案,今天想了一下发现自己还有没理解的地方,于是在此梳理一下。
什么是线程安全
说String类线程安全,那么首先要明白,什么是线程安全。
《Java并发编程实践》中对线程安全的定义:
当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。
还有一些其他的定义方式:
当多个线程访问某个方法时,不管你通过怎样的调用方式或者说这些线程如何交替的执行,我们在主程序中不需要去做任何的同步,这个类的结果行为都是我们设想的正确行为,那么我们就可以说这个类时线程安全的。 如果一段代码可以保证多个线程访问的时候正确操作共享数据,那么它是线程安全的。
其实看了上述定义之后,还是没有理解,为什么说String类不可变就是线程安全的,直到我看到了知乎上的一个问题。。。 为什么string不可变就是线程安全的?
en,就是上面这个,我也有同样疑惑。结合各个大佬的回答,重新整理了一下原因。
为什么String类不可变,就是线程安全的?
原因:String对象创建后,内容无法改变,当多个线程调用String对象里的方法操作String对象时,都会产生一个新的对象,对原对象无法做任何修改,所以也就不需要进行额外的同步操作,线程就能始终读到相同的内容。
按照我原来的理解,是这样的场景,新建一个classA,里面有一个方法,进行了以下处理:
String s=new String("abc"); //s指针指向堆1
void update(){
s=s.replace("b","d"); //线程A: s指针指向堆2
}
void read(){
System.out.println(s); //线程B: 可能读到堆1
}
由于变量s的值会变来变去,因此会出现线程读数据不同步的问题。
但这种情况不能说是因为String类是线程不安全的。而是classA所在的类是线程不安全的。
结合高赞回答,再品味一下==
class ThreadSafe{
private final String data="xxx";
}
class ThreadUnSafe1{
private String data="xxx";
}
class ThreadUnSafe2{
private final Object data= new XxxMutableObject();
}
类ThreadSafe是线程安全的, 类ThreadUnsafe1和ThreadUnSafe2是非线程安全的。