伊莉討論區

標題: 一些Java(Android)問題請益(2) [打印本頁]

作者: RainieYang    時間: 2018-10-24 09:31 AM     標題: 一些Java(Android)問題請益(2)

本帖最後由 RainieYang 於 2018-10-24 10:39 AM 編輯

關於 StringBuffer 和 StringBuilder 小弟有研究一下,並查了一些文章,也看了一點原始碼。StringBuilder是比較晚出現的,它的特徵是不確保多線程執行的安全性,所有方法都不使用synchronized保護。
而有人做了實驗,就算在完全單線程的情況下,StringBuilder 依然比 StringBuffer 速度要快(純用append()比較)。
但速度頂多就快了30%左右,如果使用其他像是insert()之類的方法去比較,兩者甚至感覺不出速度差異(因為時間主要被new物件和gc消耗掉了,所以瓶頸在別的地方)。

也就是說結論是兩者的速度並沒有差距到這麼大(數量級別),一個程式通常不會因為這點微乎其微的差距導致最終performance差很多,而StringBuffer卻能保證多線程的安全性,那為何AndroidStudio卻會建議開發者使用StringBuilder?
(一些部分文章設計者也這麼建議,理由都是StrungBuilder速度較快,但明明頂多就快30%而已啊= =)

這樣的話,如果你的程序並不是完全單線程,是有可能有多線程在修改字串,是否應該用StringBuffer較好呢?
話說使用StringBuffer在多線程的情況下是否就絕對安全呢?  有沒有人實驗過? 跟StringBuilder放在一起進行多線程存取實驗?
---------------------------------------------------------------------------------------------------
補上小弟自己做的實驗跟結論:
實驗代碼:
  1. final int MAX_COUNTER = 1000000;
  2.          long tempLong = System.currentTimeMillis();
  3.          new Thread(new Runnable() {
  4.              @Override
  5.              public void run() {
  6.                  for(int i=0;i<MAX_COUNTER;i++){
  7.                      sbu.append("aaaa");
  8.                  }
  9.              }
  10.          }).start();
  11.         for(int i=0;i<MAX_COUNTER;i++){
  12.             sbu.append("bbbb");
  13.         }
  14.         sbuSpendTime = System.currentTimeMillis() - tempLong;

  15.         tempLong = System.currentTimeMillis();
  16.         new Thread(new Runnable() {
  17.             @Override
  18.             public void run() {
  19.                 for(int i=0;i<MAX_COUNTER;i++){
  20.                     sbi.append("aaaa");
  21.                 }
  22.             }
  23.         }).start();
  24.         for(int i=0;i<MAX_COUNTER;i++){
  25.             sbi.append("bbbb");
  26.         }
  27.         sbiSpendTime = System.currentTimeMillis() - tempLong;
複製代碼
/*
*  多執行緒安全性實驗
*  StringBuffer vs StringBuilder
*  結論如下:
*  多線程:
*  1. StringBuffer 能確保安全,StringBuilder不能。
*  2. 頻繁互搶下StringBuilder 速度可比 StringBuffer 快20倍以上。
*  單線程:
*  1. StringBuffer ,StringBuilder 均安全。
*  2. StringBuilder 速度比 StringBuffer 快約30%。
*  總結:
*  確定不會有多線程互搶的話,可以用StringBulder
*  如果會有多線程互搶,而且資料需要絕對保證安全,小弟建議用StringBuffer。
* */



作者: kwj    時間: 2018-10-24 11:53 PM

本帖最後由 kwj 於 2018-10-25 12:08 AM 編輯

1. StringBuffer 的 append() 有 synchronized 保護,所以如果你問是不是絕對安全......那你應該先去了解一下 synchronized 是怎麼進行保護的。

2. 有可能快 30% 的時候,有任何理由不使用它嗎?而且使用它對你來說就只有 class 用的不同而已,操作方法完全一樣,沒有造成任何多的程式碼或複雜的邏輯,那為什麼你不想要多 30% 的速度?就算它不一定總是比較快,但如果也沒有明顯比較慢,而有時有可能比較快的話,那到底有什麼理由你那麼不想用它??更何況 30% 在實務上是個很大的差距....。
作者: RainieYang    時間: 2018-10-25 11:31 AM

kwj 發表於 2018-10-24 11:53 PM
1. StringBuffer 的 append() 有 synchronized 保護,所以如果你問是不是絕對安全......那你應該先去了解一 ...

synchronized 我的認知是加鎖保護,先進去的thread會取得該對象的鎖,其他thread會被block住,感覺上是很安全。
我會有是否絕對安全的疑問,來自這篇文章: https://www.zhihu.com/question/20101840
該篇文章一段提到:

"然后,補充一點,關于線程安全,即使你真的遇到了這樣的場景,很不幸的是,恐怕你仍然有99.99....99%的情況下沒有必要選擇stringbuffer,因為stringbuffer的線程安全,僅僅是保證jvm不拋出異常順利的往下執行而已,它可不保證邏輯正確和調用順序正確。大多數時候,我們需要的不僅僅是線程安全,而是鎖"

針對這段,小弟很納悶,synchronize保護不就是加鎖嗎? 為什麼此文章作者會有此一說,好像用StringBuffer不會上鎖一樣,小弟也做過測試了,看來是安全的。
作者: kwj    時間: 2018-10-25 12:38 PM

RainieYang 發表於 2018-10-25 11:31 AM
synchronized 我的認知是加鎖保護,先進去的thread會取得該對象的鎖,其他thread會被block住,感覺上是很 ...

這個問題...實際上需要比要多的多執行緒經驗,會比較容易體會出來。實務上多執行緒會出錯的狀況,大多不在於 synchronized 這類的東西運作失誤,而是在於開發者的邏輯錯誤。

舉例來說,synchronized 保證同時只會有一個 thread 存取 synchronized 的資源,但如果後面正有超過一個 thread 在排隊等待資源被釋放時,後面排隊的 thread 的執行順序並不保證。也就是例如 A thread 正在存取資源,接著 B thread 也想存取,然後 C thread 也想存取。按道理來說因為 B thread 先來,當 A thread 釋放資源時應該要由 B 接著進去存取,但實際上因為 synchronized 不保證順序,因此有可能是 C thread 先進去存取,B 繼續等待。這時如果 B 和 C 互相不相依,那就什麼事也沒有;但如果邏輯上是預期 B 應該要比 C 更早執行,那問題就出現了。

另一種狀況是 synchronized 保護的對象是 method,method 存取了某個 class member,但 class member 並沒有被保護,這種狀況也會導致潛在的衝突。在一般狀況下不大容易出現,但運氣很差的時候就會冒出來。

多執行緒的程式中,有很多狀況需要小心地處理這種問題,而這種問題需要相當多的經驗才能有效地判斷和處理。
作者: RainieYang    時間: 2018-10-25 02:10 PM

kwj 發表於 2018-10-25 12:38 PM
這個問題...實際上需要比要多的多執行緒經驗,會比較容易體會出來。實務上多執行緒會出錯的狀況,大多不 ...

嗯 也是呢,或許可以考慮在需要保護資料的地方改成外部手動加鎖,然後內部依然用StringBuilder?




歡迎光臨 伊莉討論區 (http://a401.file-static.com/) Powered by Discuz!