[Java 并發]并發編程實戰筆記-對象的共享

java 對象的共享

要編寫正確的并發程序,關鍵在于在訪問共享可變狀態是需要進行正確的管理,下面介紹如何共享和發布對象,從而使他們能夠安全的有多線程同時訪問。

volatile

加鎖機制既可以確保可見性有可以確保原子性,而volatile變量只能確保可見性。

典型用法

    volatile boolean asleep;
    ...
    while (!asleep)
      countSomeSheep();

非原子的64位操作

jvm起初設計的時候64位計算并不是普遍的,大部分機器還是32位的。在32位機器上計算long類型時,其實是分成高位和低位分別計算,在把結果返回。但是jvm規范并沒有強制要求這個操作時原子性的,所以在并發場景下,一個線程讀到的long可能是另一個線程只計算了高位或低位的結果。為了避免這樣的操作,需要把這個變量聲明稱volatile。

 volatile long l;

發布與溢出

先看一個例子

 //  BAD
class UnsafeStates {
  private String[] states = new String[] {"AK", "AL" ...  };
  public String[] getStates() { return states; }
}

上面的方法直接把內部變量引用返回,造成了內部變量溢出。正確的方式應該是:

 //  GOOD
class UnsafeStates {
    private String[] states = new String[]{"AK", "AL"};
    public String[] getStates() {
        // 返回副本,這樣就不會影響內部
        String[] tmp = new String[states.length];
        System.arraycopy(states, 0, tmp, 0, states.length);
        return tmp;
    }
}

封接能夠使得對程序的可見性進行分析變得可能,并使得無意中破壞設計約束條件變得更雄。

   // BAD
    public class ThisEscape {
        public ThisEscape(EventSource source) {
            source.registerListener(
                    new EventListener() {
                        public void onEvent(Event e) {
                            doSomething(e);
                        }
                    });
        }
    }

上面的例子隱式的提前暴露了this對象(在對象構造完成之前,或者說在構造方法中暴露了當前對象的引用)。在構造方法完成之前,當前對象的處于不可預測和不一致的狀態,this對象提前暴露,超出了它的所用范圍。正確的方法是可以把對象引用保存到變量中,等構造方法完成后在調用,如下;

    // GOOD
    public class SafeListener {
        private final EventListener listener;
        private SafeListener() {
            listener = new EventListener() {
                public void onEvent(Event e) {
                    doSomething(e);
                }
            };
        }
        public static SafeListener newInstance(EventSource source) {
            SafeListener safe = new SafeListener();
            source.registerListener(safe.listener);
            return safe;
        }
    }

安全對象的構造過程

線程封閉

當訪問共享的可變數據室,通常需要使用同步。一種避免使用同步的方式就是不同享數據。如果僅在但相成內訪問數據,就不需要同步。折中技術被稱為線程封閉(Thead Confinement),他是實現線程安全性的最簡單方式之一。

一種常見的應用是JDBC的Connection對象。JDBC規范并不要求Connecting對象必須是線程安全的。在典型的服務器應用程序中,線程從連接池中獲得一個Connectiion對象,并且用改對象來處理請求,使用完成后在將對象返回給連接池。由于大多數請求(servlet請求)都是有單個線程采用同步方式來處理,并且在Connection對象返回之前,連接池不會再將它分配給其他線程,因此,折中連接管理模式在處理請求是隱含地將Connection對象封閉在線程中。

Java語言中無法強制將對象封閉在某個線程中,這是程序設計需要考慮的因素。Java好辛苦提供了一些機制幫助維持線程封閉性,例如局部變量和ThreadLocal類,即便如此,程序員仍然需要負責確保封閉在線程中的對象不會從線程中溢出。

Ad-hoc 線程封閉

維護線程封閉性的職責完全由程序實現來承擔,ad-hoc線程封閉式非常脆弱的,因為沒有任何一種語言特性支持他。事實上,對線程封閉對象(例如,GUI應用程序中的可視化組建或數據模型等)的引用通常保存在公有變量中。

由于Ad-hoc線程封閉技術的脆弱性,因此在線程中盡量少用他,在可能的情況下,應該使用更強的線程封閉技術(例如,棧封閉或ThreadLocal類)。

棧封閉

棧封閉式一個對象只能通過本地變量訪問。就像封裝更容易保存不變量,本地變量可以更容易限定線程對變量的訪問。本地變量的本質是限定在當前線程內,他存在于執行線程棧中,不能被其他線程訪問。棧封閉(又叫 within-thread 或 thread-local,不要和ThreadLocal類混了)更容易維護并且比Ad-hoc線程封閉更強壯。

通俗一點講,本地變量是變量作用域最小的,在開發多線程程序是應該盡量減小變量的作用域。

不變性

如果一個對象發布以后不會發生變化,那么在訪問他的時候就不用考慮線程安全問題了。

**不可變對象一定是線程安全的 **

當滿足一下條件時,對象才是不可變的:

  • 對象創建以后七狀態就不能I許改。
  • 對象的所有與都是final類型
  • 對象是正確創建的(在對象的創建其間,this引用沒有溢出)。

安全發布

上節講的是如何把對象封閉在線程或另一個對象的內部,確保對象不被發布。

要安全地發布一個對象,對象的引用以及對象的狀態必須同時對其他線程可見。一個正確構造的對象可以通過一下方式來安全的發布:

  • 在靜態初始化函數中初始化一個對象引用
  • 講對象的引用保存到volatile類型的域或者AtomicRefer對象中。
  • 講對象的引用保存到謳歌正確構造對象的final類型域中g- 講對象的引用保存到一個有鎖保護的域中。

可變對象在構造后可以,那么安全發布只能確保“發布當時”狀態的可見性。

對象的發布需求取決于他的可變性:

  • 不可變對象可以通過任意機制來發布。
  • 事實不可變對象必須通過安全方式來發布
  • 可變對象必須通過安全方式來發布,必須通過是線程安全的或者有某個鎖保護起來。

在并發程序中使用和共享對象是,可以使用一些實用的策略,包括:
線程封閉。線程封閉的對象只能由一個線程擁有,對象被封閉在改線程中,并且只能由一個線程修改。
制度共享。在沒有額外同步的情況下,共享的制度對象可以有多線程并發訪問,但任何線程都不能修改I啊它。共享的制度對象包括不可變對象和事實不可變對戲那個。
線程安全共享。線程安全的對象在其內部實現同步,因此多個線程可以通過對象的共有接口進行訪問和不需要進一步的同步。
保護對象。被保護的對象只能通過持有特定的鎖來訪問。保護對象包括封裝在其他線程安全對象中的對象,以及已發布的并且由某個特定鎖保護的對象。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,527評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,687評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,640評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,957評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,682評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,011評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,009評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,183評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,714評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,435評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,665評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,148評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,838評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,251評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,588評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,379評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,627評論 2 380

推薦閱讀更多精彩內容