3 回答

TA贡献2039条经验 获得超7个赞
我认为可以总结如下:
对
String
对象的操作是线程安全的。(它们是线程安全的,因为String
对象是不可变的,但为什么与您的示例没有直接关系。)非共享变量2上的非同步读取和写入操作1不是线程安全的,无论变量的类型如何。
final
你的例子确实如此str = str + 1;
。它将String
对象操作与非同步共享变量 ( str
) 的操作结合起来。由于后者,它不是线程安全的。
1 - 更准确地说,在写入和读取之间没有发生关系的操作以保证所需的内存可见性属性,并且没有锁定来保证所需的原子性属性。(“必需”是指算法正确性所必需的......)
2 - 共享意味着对多个线程可见和使用。如果一个变量只对一个线程可见或被一个线程使用,则称它是线程受限的,实际上它是不共享的。

TA贡献1775条经验 获得超11个赞
了解内存在编程语言中的工作原理很重要。变量str不是一个字符串对象,就像你想的那样。但它是对带有一些地址的字符串对象的引用。
修改str指向的内容,不会修改它指向的字符串。事实上发生的事情是这样的:
我们有一个内存池,在我们的池中是三个字符串。每个字符串都有一个地址,可以让我们找到它。
字符串 1 - “你好”,地址:0x449345
字符串 2 - “那里”,地址:0x058345
字符串 3 - “世界”,地址:0x004934
我们有一个指向每个变量的变量,我们称它们为 a、b 和 c。
如果我们说:System.out.println(a);
Java 会打印Hello
. 但是a不是"Hello"。相反, a 是包含0x449345的东西。电脑接着说:“好的,我去把 0x449345 的内容打印出来。” 当它查看该地址时,它会找到字符串“Hello”。
但是,如果您说:a = "NEW STRING";
a 不会指向我们以前的任何地址。而是创建一个新地址,并将“新字符串”放置在该内存位置内。
这也是垃圾收集在 Java 中的工作方式。一旦你设置一个等于“NEW STRING”的值,它就不再指向 0x449345,这告诉垃圾收集器该对象可以安全删除。这就是您的程序自行清理的方式,并且不会占用大量 RAM。
因此,指向字符串的引用不是线程安全的,而是实际的对象!任何不可变对象都是安全的,因为您根本无法修改该对象,您只能修改变量指向的内容。您必须完全指向一个不同的对象才能“修改”您的不可变对象。

TA贡献1779条经验 获得超6个赞
您的str引用不是一成不变的,每次重新分配它的值时都会对其进行变异。由于您在没有同步或互斥锁的线程之间共享可变状态,因此结果不安全。
以下为我尝试了 5 次。注意我在连接你的字符串时添加了互斥锁。
public class QuickyTest {
private static String str = "1";
public static void main( String[] args ) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool( 10 );
IntStream.range( 0, 1000 ).forEach( ( i ) -> executorService.submit( () -> {
append( "1" );
}
) );
executorService.awaitTermination( 10, TimeUnit.SECONDS );
System.out.println( str.length() );
executorService.shutdown();
}
private static synchronized void append( String s ) {
str = str + s;
}
}
总是打印“1001”。
添加回答
举报