亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

字符串線程安全嗎?

字符串線程安全嗎?

喵喵時光機 2022-07-14 09:41:27
字符串是不可變的,這意味著一旦您修改了值,它就會創建一個新的引用并保持之前的引用值不變。但是,當有人爭論時,我不明白:字符串是線程安全的,因為它們是不可變的考慮下面的代碼:private String str = "1";ExecutorService executorService = Executors.newFixedThreadPool(10);IntStream.range(0, 1000).forEach((i)-> executorService.submit(()-> {    str = str +"1";}));executorService.awaitTermination(10, TimeUnit.SECONDS);System.out.println(str.length());如果它是線程安全的,那么它應該打印1001,而它總是打印小于預期值。我知道上面的代碼將創建1001不可變的引用,每個引用本身都是線程安全的,但作為開發人員,仍然不能使用不可變的東西并期望它end-result是線程安全的。恕我直言,不變性不能保證線程安全。有人可以向我解釋一下 String 是如何線程安全的嗎?更新:感謝您的回答,我知道每個字符串都可以是線程安全的,但我的觀點是,當您在其他方法中使用它們時,線程安全性和不變性之間沒有直接關系。例如,不可變對象可以在有狀態對象中使用并以非線程安全結果結束,而可變對象也可以在同步方法中使用并以線程安全結果結束。
查看完整描述

3 回答

?
largeQ

TA貢獻2039條經驗 獲得超8個贊

我認為可以總結如下:

  1. String對象的操作是線程安全的。(它們是線程安全的,因為 String對象是不可變的,但為什么與您的示例沒有直接關系。)

  2. 非共享變量2上的非同步讀取和寫入操作1不是線程安全的,無論變量的類型如何。final

你的例子確實如此str = str + 1;。它將String對象操作與非同步共享變量 ( str) 的操作結合起來。由于后者,它不是線程安全的。


1 - 更準確地說,在寫入和讀取之間沒有發生關系的操作以保證所需的內存可見性屬性,并且沒有鎖定來保證所需的原子性屬性。(“必需”是指算法正確性所必需的......)

2 - 共享意味著對多個線程可見和使用。如果一個變量只對一個線程可見或被一個線程使用,則稱它是線程受限的,實際上它是不共享的。


查看完整回答
反對 回復 2022-07-14
?
繁星淼淼

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。

因此,指向字符串的引用不是線程安全的,而是實際的對象!任何不可變對象都是安全的,因為您根本無法修改該對象,您只能修改變量指向的內容。您必須完全指向一個不同的對象才能“修改”您的不可變對象。


查看完整回答
反對 回復 2022-07-14
?
哆啦的時光機

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”。


查看完整回答
反對 回復 2022-07-14
  • 3 回答
  • 0 關注
  • 228 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號