查看完整版本: 一些Java(Android)問題請益(2)
頁: [1]

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放在一起進行多線程存取實驗?
---------------------------------------------------------------------------------------------------
補上小弟自己做的實驗跟結論:
實驗代碼:
final int MAX_COUNTER = 1000000;
         long tempLong = System.currentTimeMillis();
         new Thread(new Runnable() {
             @Override
             public void run() {
                 for(int i=0;i<MAX_COUNTER;i++){
                     sbu.append("aaaa");
                 }
             }
         }).start();
        for(int i=0;i<MAX_COUNTER;i++){
            sbu.append("bbbb");
        }
        sbuSpendTime = System.currentTimeMillis() - tempLong;

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


...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div><div></div>

kwj 發表於 2018-10-24 11:53 PM

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

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

2. 有可能快 30% 的時候,有任何理由不使用它嗎?而且使用它對你來說就只有 class 用的不同而已,操作方法完全一樣,沒有造成任何多的程式碼或複雜的邏輯,那為什麼你不想要多 30% 的速度?就算它不一定總是比較快,但如果也沒有明顯比較慢,而有時有可能比較快的話,那到底有什麼理由你那麼不想用它??更何況 30% 在實務上是個很大的差距....。...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

RainieYang 發表於 2018-10-25 11:31 AM

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

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

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

針對這段,小弟很納悶,synchronize保護不就是加鎖嗎? 為什麼此文章作者會有此一說,好像用StringBuffer不會上鎖一樣,小弟也做過測試了,看來是安全的。...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

kwj 發表於 2018-10-25 12:38 PM

RainieYang 發表於 2018-10-25 11:31 AM static/image/common/back.gif
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 並沒有被保護,這種狀況也會導致潛在的衝突。在一般狀況下不大容易出現,但運氣很差的時候就會冒出來。

多執行緒的程式中,有很多狀況需要小心地處理這種問題,而這種問題需要相當多的經驗才能有效地判斷和處理。...<div class='locked'><em>瀏覽完整內容,請先 <a href='member.php?mod=register'>註冊</a> 或 <a href='javascript:;' onclick="lsSubmit()">登入會員</a></em></div>

RainieYang 發表於 2018-10-25 02:10 PM

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

嗯 也是呢,或許可以考慮在需要保護資料的地方改成外部手動加鎖,然後內部依然用StringBuilder?<br><br><br><br><br><div></div>
頁: [1]