3物件的共享

2021-08-03 22:35:09 字數 3883 閱讀 9355

作用:將當前執行緒對volatile的改變立即通知給其他執行緒;保證了volatile變數對執行緒的可見性;volatile是一種比synchronizyed稍弱的同步機制

對可見性的影響:volatile變數對可見性的影響比volatile變數本身更為重要。當執行緒a首先寫入乙個volatile變數並且執行緒b隨後讀取該變數時,在寫入volatile變數之前對a可見的所有變數(包括volatile變數)的值,在b讀取了volatile變數後,對b也是可見的。因此,從記憶體可見性的角度來看,寫入volatile變數相當於退出同步**塊,而讀取volatile變數就相當於進入同步**塊

典型用法:檢查某個狀態標記以判斷是否退出迴圈

注意:volatile的語義不足以確保遞增操作(count++)的原子性,除非你能確保只有乙個執行緒對變數執行寫操作;原子變數提供了「讀-改-寫」的原子操作,並且嚐嚐做一種「更好的volatile變數」;即volatile只能確保可見性,二加鎖機制既可以確保可見性又可以確保原子性

使用volatile變數的條件

發布(publish):使物件能夠在當前作用於之外的**中使用

溢位(escape):當某個不應該發布的物件被發布時就成為溢位

發布方式

//方式一:將指向物件的引用儲存到其他**能訪問到的地方

//發布了new hashset()物件

public static setknowsecrets;

public void initialize()

//方式二:在某乙個非私有的方法中返回物件引用

//發布了new hashset()物件

public setgetsecrets()

//方式三:將物件的引用傳遞到其他類的方法中

//發布了new user()物件

public class caculate

}caculate.caculate(new user());

//方式四:在已發布的物件中的非私有域中引用物件

//發布了"ak","al"

class unsafestates ;

public string getstates()

}//方式五:在類的方法內發布匿名內部類;因為匿名內部類包含了當前物件的隱含引用this,隨意發布匿名內部類時也發布了自己

//發布了new eventlistener(),在該物件內部包含自己的隱試引用this,即當前thisescape物件

public class thisescape

});//尚未構造完成

value = 10;

}private void dosomething(object o)

}

4.物件發布的風險:無論其他執行緒會對已發布的引用執行何種操作,其實都不重要,因為誤用該引用的風險始終存在。當某個物件逸出後,你必須假設有某各類或者執行緒可能會誤用該物件。這正是需要使用封裝的最主要原因:封裝能夠使得對程式的正確性分析變得可能,並使得無意中破壞設計約束條件變得更難

不正確構造

常見不正確構造

解決不安全構造的方法

/**

* 用乙個私有的建構函式和乙個工友的工廠方法

* 建構函式用來例項化物件

* 工廠方法發布已經構造完的this,並返回這個構造完的例項

*/public class safelistener };}

public static safelistener newinstance(eventsource source)

}

概念:當訪問共享的可變資料時,通常需要同步;一種避免使用同步的方式就是不共享資料;如果僅在單執行緒內訪問資料,就不需要同步;這就叫執行緒封閉

作用:當某個物件封閉在乙個執行緒中時,這種用法將自動實現執行緒安全性,即使被封裝的物件本身不是執行緒安全的;執行緒封閉是實現執行緒安全的最簡單方式之一

執行緒封閉的方式

ad-hoc執行緒封閉:維護執行緒封閉性的職責完全由程式實現來承擔

棧封閉:棧封閉是一種特殊的執行緒封閉,在棧封閉中將物件定義為區域性變數,區域性變數的固有屬性之一就是封閉在執行執行緒中

引用型別的區域性變數,為了維持執行緒封閉,需要多做一些工作以確保被引用的物件不會溢位當前執行緒

threadlocal

不可變物件:如果某個物件在被建立後其狀態就不能被修改,那麼這個物件就成為不可變物件

不可變物件固有屬性:執行緒安全性是不可變物件的固有屬性之一,他們的不變性條件是由建構函式建立的,只要他們的狀態不改變,那麼這些不變性條件就能得以維持

不可變物件的條件

物件建立以後其狀態就不能修改

物件的所有域都是final型別

物件是正確構造的(在物件構造期間,this引用沒有逸出)

習慣:正如「除非需要更高的可見性,否則應將所有的域都宣告為私有域」是乙個良好的程式設計習慣,「除非需要某個域是可變的,否則應將其宣告為final域」也是乙個良好的程式設計習慣

示例

/**

* 注意:在構造和返回不變狀態物件時要和執行緒區域性變數斷開關聯,尤其是引用變數

* 下面注釋的兩個arrays.copyof操作就是

*/class onevaluecache

public biginteger getfactors(biginteger i)

}public class volatilecachedfactorizer implements servlet

encodeintoresponse(resp, factors);

}}

前提:物件被正確構造

安全發布機制:

在靜態**塊或者靜態域中初始化乙個物件的引用

將物件的引用儲存到volatile型別的域

將物件的引用儲存到atomicreference型別的域中

將物件的引用儲存到final型別的域中

將物件的引用儲存到乙個由鎖保護的域中

通過執行緒安全庫的容器類發布物件

執行緒安全庫提供的安全發布保證

通過將乙個鍵或者值放入hashtable、synchronizecdmap或者concurrentmap中,可以安全地將它發布給任何從這些容器中訪問它的執行緒(無論是直接訪問還是通過迭代器訪問)

通過將某個元素放入vector、copyonwritearraylist、copyonwritearrayset、synchronizedlist或synchronizedset中,可以將元素安全地發布到任何從這些容器中訪問元素的執行緒

通過將某個元素放入blockingqueue或者concurrentlinkedqueue中,可以將元素安全地發布到任何從這些佇列中訪問元素的執行緒

類庫中的其他資料傳遞機制(例如futrue和exchanger)同樣能實現安全發布

概念:如果物件從技術上來看是可變的,但其狀態在發布後不會再改變,那麼這種物件稱為「事實不可變物件(effectively immutable object)」

結論1:在沒有額外同步的情況下,任何執行緒都可以安全地使用被安全發布的事實不可變物件

結論2:通過使用事實不可變物件,不近可以簡化開發過程,而且還能由於減少了同步而提高效能

不可變物件可以通過任意機制來發布

事實不可變物件必須通過安全的發布機制來發布

可變物件必須通過安全的發布機制來發布,並且必須是執行緒安全的或者有某個鎖保護起來

執行緒封閉:執行緒封閉的物件只能由乙個執行緒擁有,物件被封閉在該執行緒中,並且只能由這個執行緒修改

唯讀共享:在沒有額外同步的情況下,共享的唯讀物件可以有多個執行緒併發訪問,但任何執行緒都不能修改它。共享的唯讀物件包括不可變物件和事實不可變物件

執行緒安全共享:執行緒安全的物件在其內部實現同步,因此多個執行緒可以通過物件的公有介面進行訪問而不需要進一步的同步

保護物件:被保護的物件只能通過持有特定的鎖來訪問。保護物件包括封裝在其他安全物件中的物件,以及已發布的並且有某個特定的鎖保護的物件

java執行緒學習 2 物件的共享

編寫安全的執行緒就是對共享資料的操作不出現問題,通過同步使來避免多個執行緒同一時刻方位共享資料,或者共享和發布物件,從而使能夠安全地由多個執行緒同時訪問。可見性 public class novisibility public static void main string args 這段 不推薦這...

併發程式設計學習 2 物件的共享

思維導圖 本文主要介紹類的域變數被多個執行緒共享時所導致的可見性問題。我所理解的可見性是指類的域變數在某一線程中變化時能夠及時準確的被其他執行緒所看見。文章還是分成兩個部分進行描述 理論部分 包括如何判斷域變數是否可見,會導致什麼樣的問題,又該如何解決。使用部分 通過 不發布物件 發布但不可修改 安...

as2 AVM1物件和as3物件的通訊

注意本人遇到的 connection名稱問題,因為實際上是域 名稱,呼叫時有時會找不到,因此在connection名稱前加 更真實的原因是as1.0不支援connection,所以發布時一定要看清楚哈,用flash player6不一定是as2.0 具有不可預知網域名稱的不同域。有時候,可能希望具有...