日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

JAVA并发编程实战---第三章:对象的共享

發(fā)布時(shí)間:2025/5/22 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JAVA并发编程实战---第三章:对象的共享 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

  在沒(méi)有同步的情況下,編譯器、處理器以及運(yùn)行時(shí)等都可能對(duì)操作的執(zhí)行順序進(jìn)行一些意想不到的調(diào)整。在缺乏足夠同步的多線程程序中,要對(duì)內(nèi)存操作的執(zhí)行順序進(jìn)行判斷幾乎無(wú)法得到正確的結(jié)果。

非原子的64位操作

  當(dāng)線程在沒(méi)有同步的情況下讀取變量時(shí),可能會(huì)讀到一個(gè)失效值,但至少這個(gè)值是由之前的某個(gè)線程設(shè)置,而不是一個(gè)隨機(jī)值。這種安全性保證也被稱為最低安全性。

  Java內(nèi)存模型要求:變量的讀取操作和寫入操作都必須是原子操作,但對(duì)于非Volatile類型的long和Double變量,JVM允許將64的讀操作或?qū)懖僮?#xff0c;分解成兩個(gè)32位的操作。因此,及時(shí)不考慮失效數(shù)據(jù)問(wèn)題,在多線程中使用共享且可變的long或double等類型的變量也是不安全的,除非使用Volatile關(guān)鍵字或鎖保護(hù)起來(lái)。

  加鎖的含義不僅僅局限于互斥行為,還包括內(nèi)存可見(jiàn)性。為了確保所有線程都能看到共享變量的最新值,所有執(zhí)行讀操作或?qū)懖僮鞯木€程必須在同一個(gè)鎖上同步。

Volatile

  Volatile變量用來(lái)確保將變量的更新操作通知到其他線程。當(dāng)把變量申明為Volatile類型后,編譯器與運(yùn)行時(shí)都會(huì)注意到這個(gè)變量是共享變量,因此不會(huì)講該變量上的操作與其他內(nèi)存操作一起重排序。Volatile變量不會(huì)被緩存在寄存器或者其他處理器不可見(jiàn)的地方,一次讀取Volatile類型的變量總是會(huì)返回最新寫入的值。

  加鎖機(jī)制既可以確保可見(jiàn)性又可以確保原子性,而Volatile變量只能確保可見(jiàn)性。

  當(dāng)且僅當(dāng)滿足以下所有條件時(shí),才應(yīng)該使用Volatile變量:

  • 對(duì)變量的寫入操作不依賴與變量的當(dāng)前值,或者能確保只有單個(gè)線程更新變量的值。
  • 該變量不會(huì)與其他狀態(tài)變量一起納入不變性條件中。
  • 在訪問(wèn)變量時(shí)不需要加鎖。

  對(duì)象的發(fā)布與逸出

  發(fā)布:“發(fā)布”一個(gè)對(duì)象是指,使對(duì)象能夠在當(dāng)前作用域之外的代碼中使用。

  例如:將一個(gè)指向該對(duì)象的引用保存到其他代碼可以訪問(wèn)的地方,或者在某一個(gè)非私有的方法中返回該對(duì)象的引用,或者將引用傳遞到其他類的方法中。

  代碼示范:將一個(gè)指向該對(duì)象的引用,保存到其他代碼可以訪問(wèn)的地方。

// 保存在一個(gè)共有的靜態(tài)變量中 public static Set<Secret> knowSecrets; public void initialize(){ knowSecrets = new HashSet<Secret>(); }

  逸出:“逸出”是指,某個(gè)不應(yīng)該發(fā)布的對(duì)象被發(fā)布出去。

  當(dāng)發(fā)布某個(gè)對(duì)象時(shí),可能會(huì)間接地發(fā)布其他對(duì)象。例如上述代碼,若將一個(gè) Secret 對(duì)象添加到 knownSecret 中,那么會(huì)發(fā)布這個(gè)?Secret 對(duì)象;因?yàn)槿魏未a都可以遍歷這個(gè)集合,并獲得對(duì)??Secret 對(duì)象的引用。同樣,如果從非私有方法中返回一個(gè)引用,則會(huì)發(fā)布返回對(duì)象。

  代碼示范:某一個(gè)非私有的方法中返回私有對(duì)象的引用。

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

  上述代碼中,數(shù)組 states 已經(jīng)逸出了它所在的作用域,因?yàn)檫@個(gè)本應(yīng)是私有的變量,已經(jīng)被發(fā)布了。

  逸出范圍:當(dāng)一個(gè)對(duì)象A被發(fā)布時(shí),在A的非私有域中引用的所有對(duì)象同樣會(huì)被發(fā)布。也就是,一個(gè)已經(jīng)發(fā)布的對(duì)象A,能夠通過(guò)非私有的變量引用和方法調(diào)用到達(dá)其他的對(duì)象,那么所能夠到達(dá)的對(duì)象,均會(huì)跟隨A一起發(fā)布。

  此處給出一個(gè)定義:“外部方法”。假如有一個(gè)類C,對(duì)于C來(lái)說(shuō),“外部方法”是指行為不完全由C來(lái)規(guī)定的方法,包括其他類中定義的方法以及類C中可以被改寫的方法(既不是[private]方法,也不是[final]方法)。

  當(dāng)把一個(gè)對(duì)象傳遞給外部方法時(shí),則該對(duì)象就會(huì)面臨一定的危險(xiǎn),因?yàn)槟悴恢劳獠糠椒〞?huì)對(duì)該對(duì)象做些什么,因此我們需要使用封裝。封裝能夠使得對(duì)程序的正確性進(jìn)行分析變得可能,并使得無(wú)意中破壞設(shè)計(jì)約束條件變得困難。

  工廠方法避免this引用在構(gòu)造方法中逸出:

  首先了解 this 引用是如何在構(gòu)造方法中逸出的。先看一段代碼:發(fā)布一個(gè)內(nèi)部類的實(shí)例。

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

  上述代碼中,當(dāng) ThisEscape 發(fā)布 EventListener 時(shí),也隱含發(fā)布了 ThisEscape 實(shí)例本身,因?yàn)檫@個(gè)內(nèi)部類的實(shí)例中包含了對(duì) ThisEscapse 實(shí)例的隱含引用。只要其他線程在ThisEscape未構(gòu)造之前(構(gòu)造返回狀態(tài))調(diào)用這個(gè)類,那么this就會(huì)被新建線程共享并識(shí)別它(線程溢出)。

  下面的代碼對(duì)上面的示例進(jìn)行解釋:

public class ThisEscape {int i = 100; public ThisEscape(int j){ new Thread(new Runnable() { @Override public void run() { System.out.println(i + j); } }).start(); } public static void main(String[] args) { ThisEscape thisE = new ThisEscape(100); } }

  上述代碼給出了逸出的一個(gè)特殊示例,即this引用在構(gòu)造方法中逸出。當(dāng)內(nèi)部的new Thread發(fā)布時(shí),在外部封裝的ThisEscape實(shí)例也逸出了,于是可以取到 i 值與 j 進(jìn)行計(jì)算。因此當(dāng)從對(duì)象的構(gòu)造方法中發(fā)布對(duì)象時(shí),只是發(fā)布了一個(gè)尚未構(gòu)造完成的對(duì)象。

  在構(gòu)造方法中使用 this 引用逸出的常見(jiàn)錯(cuò)誤:在構(gòu)造方法中啟動(dòng)一個(gè)線程;在構(gòu)造方法中調(diào)用一個(gè)可改寫的實(shí)例方法。

  如果想在構(gòu)造方法中注冊(cè)一個(gè)事件監(jiān)聽(tīng)器或啟動(dòng)線程,那么可以使用一個(gè)私有的構(gòu)造方法和一個(gè)公共的工廠方法,從而避免不必要的構(gòu)造過(guò)程。如以下程序所示:

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; } }

  上面的代碼為在構(gòu)造方法中注冊(cè)一個(gè)事件監(jiān)聽(tīng)器,新建的線程無(wú)法在構(gòu)造方法之前共享和識(shí)別 safe。

轉(zhuǎn)載于:https://www.cnblogs.com/wxgblogs/p/5462869.html

《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的JAVA并发编程实战---第三章:对象的共享的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。