3 回答

TA貢獻2039條經驗 獲得超8個贊
我認為可以總結如下:
對
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 的內容打印出來?!?nbsp;當它查看該地址時,它會找到字符串“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”。
添加回答
舉報