java對象不會被改變_Java 并發編程(二)對象的不變性和安全的公布對象

二、安全公布

到眼下為止,我們重點討論的是怎樣確保對象不被公布,比如讓對象封閉在線程或還有一個對象的內部。當然,在某些情況下我們希望在多個線程間共享對象,此時必須確保安全地進行共享。然而,假設僅僅是像以下程序那樣將對象引用保存到公有域中,那么還不足以安全地公布這個對象。

//不安全的公布

public Holder holder;

public void initialize() {

holder = new Holder(42);

}

你可能會奇怪。這個看似沒有問題的演示樣例何以會執行失敗。

因為存在可見性問題,其它線程看到的Holder對象將處于不一致的狀態,即便在該對象的構造函數中已經正確地構建了不變性條件。這樣的不對的公布導致其它線程看到尚未創建完畢的對象。

不對的公布:正確的對象被破壞

你不能指望一個尚未被全然創建的對象擁有完整性。某個觀察該對象的線程將看到對象處于不一致的狀態。然后看到對象的狀態突然發生變化,即使線程在對象公布后還沒有改動過它。其實,假設以下程序中的Holder使用前面程序中的不安全公布方式,那么還有一個線程在調用assertSanity時將拋出AssertionError。

public class Holder {

private int n;

public Holder(int n) { this.n = n; }

public void assertSanity() {

if (n != n)

throw new AssertionError("This statement is false.");

}

}

因為沒有使用同步來確保Holder對象對其它線程可見,因此將Holder稱為“未被正確公布”。在未被正確公布的對象中存在兩個問題。

首先,除了公布對象的線程外,其它線程能夠看到的Holder域是一個失效值。因此將看到一個空引用或者之前的舊值。

然而,更糟糕的情況是,線程看到Holder引用的值是最新的,但Holder狀態的值卻是失效的。情況變得更加不可預測的是,某個線程在第一次讀取域時得到失效值,而再次讀取這個域時會得到一個更新值。這也是assertSainty拋出AssertionError的原因。

假設沒有足夠的同步,那么當在多個線程間共享數據時將發生一些很奇怪的事情。

不可變對象與初始化安全性

因為不可變對象是一種很重要的對象,因此Java內存模型為不可變對象的共享提供了一種特殊的初始化安全性保證。我們已經知道,即使某個對象的引用對其它線程是可見的,也并不意味著對象狀態對于使用該對象的線程來說一定是可見的。為了確保對象狀態能呈現出一致的視圖,就必須使用同步。

還有一方面。即使在公布不可變對象的引用時沒有使用同步,也仍然能夠安全地訪問該對象。

為了維持這樣的初始化安全性的保證。必須滿足不可變性的全部需求:狀態不可改動,全部域都是final類型。以及正確的構造過程。(假設Holder對象是不可變的,那么即使Holder沒有被正確地公布。在assertSanity中也不會拋出AssertionError。

)

不論什么線程都能夠在不須要額外同步的情況下安全地訪問不可改變對象,即使在公布這些對象時沒有使用同步。

這樣的保證還將延伸到被正確創建對象中全部final類型的域。在沒有額外同步的情況下,也能夠安全地訪問final類型的域。然而。假設final類型的域所指向的是可變對象。那么在訪問這些域所指向的對象的狀態時仍然須要同步。

安全公布的經常使用模式

可變對象必須通過安全的方式來公布,這通常意味著在公布和使用該對象的線程時都必須使用同步。

如今,我們將重點介紹怎樣確保使用對象的線程可以看到該對象處于已公布的狀態。并稍后介紹怎樣在對象公布后對其可見性進行改動。

安全地公布一個對象。對象的應用以及對象的狀態必須同一時候對其它線程可見。一個正確構造的對象能夠通過下面方式來安全地公布:

在靜態初始化函數中初始化一個對象引用

將對象的應用保存到volatile類型的域或者AtomicReferance對象中

將對象的引用保存到某個正確構造對象的final類型域中

將對象的引用保存到一個由鎖保護的域中。

在線程安全容器內部的同步意味著,在將對象放入到某個容器。比如Vector或synchronizedList時,將滿足上述最后一條需求。假設線程A將對象X放入一個線程安全的容器。隨后線程B讀取這個對象,那么能夠確保B看到A設置的X狀態,即便在這段讀/寫X的應用程序代碼中沒有包括顯式的同步。雖然Javadoc在這個主題上沒有給出非常清晰的說明,但線程安全庫中的容器類提供了下面的安全公布保證:

通過將一個鍵或者值放入Hashtable、synchronizedMap或者ConcurrentMap中,能夠安全地將它公布給不論什么從這些容器中訪問它的線程(不管是直接訪問還是通過迭代器訪問)

通過將某個元素放入Vector、CopyOnWriteArrayList、CopyOnWriteArraySet、synchronizedList或synchronizedSet中,能夠將該元素安全地公布到不論什么從這些容器中訪問該元素的線程

通過將某個元素放入BlockingQueue或者ConcurrentLinkedQueue中,能夠將該元素安全地公布到不論什么從這些隊列中訪問該元素的線程。

類庫中的其它數據傳遞機制(比如Future和Exchanger)相同能實現安全公布。在介紹這些機制時將討論它們的安全公布功能。

通常,要公布一個靜態構造的對象,最簡單和最安全的方式是使用靜態的初始化器:

public static Holder holder = new Holder(42);

靜態初始化器由JVM在類的初始化階段運行。

因為在JVM內部存在著同步機制,因此通過這樣的方式初始化的不論什么對象都能夠被安全地公布[JLS 12.4.2]。

事實不可變對象

假設對象在公布后不會被改動,那么對于其它在沒有額外同步的情況下安全地訪問這些對象的線程來說,安全公布是足夠的。全部的安全公布機制都能確保。當對象的引用對全部訪問該對象的線程可見時,對象公布時的狀態對于全部線程也將是可見的,而且假設對象狀態不會再改變,那么就足以確保不論什么訪問都是安全的。

假設對象從技術上來看是可變的,但其狀態在公布后不會再改變。那么把這樣的對象稱為“事實不可變對象(Effectively Immutable Object)”。這些對象不須要滿足之前提出的不可變性的嚴格定義。在這些對象公布后。程序僅僅需將它們視為不可變對象就可以。通過使用事實不可變對象。不僅能夠簡化開發過程,并且還能因為降低了同步而提高性能。

在沒有額外的同步的情況下,不論什么線程都能夠安全地使用被安全公布的事實不可變對象。

比如,Date本身是可變的,但如果將它作為不可變對象來使用,那么在多個線程之間共享Date對象時,就能夠省去對鎖的使用。

如果須要維護一個Map對象,當中保存了每位用戶的近期登錄時間:

public Map lastLogin =Collections.synchronizedMap(new HashMap());

假設Date對象的值在被放入Map后就不會改變,那么synchronizedMap中的同步機制就足以使Date值被安全地公布。而且在訪問這些Date值時不須要額外的同步。

可變對象

假設對象在構造后能夠改動,那么安全公布僅僅能確保“公布當時”狀態的可見性。對于可變對象,不僅在公布對象時須要使用同步,并且在每次對象訪問時相同須要使用同步來確保興許改動操作的可見性。

要安全地共享可變對象。這些對象就必須被安全地公布。并且必須是線程安全的或者由某個鎖保護起來。

對象的公布需求取決于它的可變性:

不可變對象能夠通過隨意機制來公布

事實不可改變必須通過安全方式公布

可變對象必須通過安全方式公布。而且必須是線程安全的或者由某個鎖保護起來

安全的共享對象

當獲得對象的一個引用時,你須要知道在這個引用上能夠運行哪些操作。

在使用它之前是否須要獲得一個鎖?能否夠改動它的狀態,或者僅僅能讀取它?很多并發錯誤都是因為沒有理解共享對象的這些“既定規則”而導致的。當公布一個對象時,必須明白地說明對象的訪問方式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371667.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371667.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371667.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

nginx 上php不可寫解決方法

在php.ini中設置的session.save_path會被php-fpm.conf中覆蓋 打開php-fpm.conf文件找到php_value[session.save_apth] 這里的/var/lib/php/session 為實際的session保存目錄,設置為777,必須讓其他用戶有rw權限,因為php在Linux里面以其他用戶身份運行(匿名…

JavaOne 2012:Java策略主題演講和IBM主題演講

與 JavaOne 2010 相似,我對JavaOne 2012的開始也很艱難。由于“計算機和打印機技術上的困難”,辦理登機手續的人花了70分鐘為我提供JavaOne徽章。 盡管我不是世界上最有耐心的人,但比等待更令人失望的是,我錯過了參加“社區會議&a…

java citymap_Java實現Map集合二級聯動

Map集合可以保存鍵值映射關系,這非常適合本實例所需要的數據結構,所有省份信息可以保存為Map集合的鍵,而每個鍵可以保存對應的城市信息,本實例就是利用Map集合實現了省市級聯選擇框,當選擇省份信息時,將改變…

【NIO】之IO和NIO的區別

在Java1.4之前的版本,Java對I/O的支持并不完善,開發人員在開發高性能I/O程序的時候,會面臨以下幾個問題: 1、沒有數據緩存區,I/O性能存在問題 2、沒有C/C通道的概念,輸入和輸出流是相互獨立的不能復用 3、同…

Mono環境下訪問SSL

由于MONO沒有CA證書,所以訪問SSL鏈接(HTTPS)就會出錯,這時候只要強制訪問就可以。 using System.Net.Security;using System.Security.Authentication;using System.Security.Cryptography.X509Certificates; private static bool…

JavaOne 2012:使用HTML5和Java構建移動應用程序

我返回了Parc 55 (任務會議室),觀看Max Katz的( Exadel開發人員關系)“用HTML5和Java構建移動應用程序” Bird-of-Feather(BoF)演示。 具體來說,Katz在Tiggzi (基于云的應…

HDU 2602.Bone Collector-動態規劃0-1背包

Bone Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 85530 Accepted Submission(s): 35381 Problem DescriptionMany years ago , in Teddy’s hometown there was a man who was called “Bone Col…

java線程實現排序_【多線程實現快速排序】

快速排序算法實現文件QuickSort.javapackage quick.sort;import java.util.concurrent.Callable;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class QuickSort implements Callable{private int[] array;private final in…

使用Gitolite搭建Gitserver

Gitolite是一款Perl語言開發的Git服務管理工具。通過公鑰對用戶進行認證。并可以通過配置文件對些操作進行基于分支和路徑的精細控制。Gitolite採用的是SSH協議而且使用SSH公鑰認證。因此不管是管理員還是普通用戶。都須要對SSH有所了解。Gitolite的官網是:https://…

java任務分支和合并_合并/分支戰略

我會給出與Adarsh Shah相同的建議,因為在大多數情況下,2個分支(MAIN,RELEASE)就足夠了,并且使用feature branches用于你不想立即提交到MAIN的東西,因為它需要一段時間才能完全準備好測試 . 通過RELEASE,我指…

Spring安全:防止暴力攻擊

Spring Security可以為您做很多事情。 帳戶被封鎖,密碼鹽。 但是蠻力阻斷劑呢? 那是你必須自己做的。 幸運的是,Spring是一個非常靈活的框架,因此對其進行配置并不是什么大問題。 讓我向您展示一些如何針對Grails應用程序執行…

NopCommerce計劃任務

NopCommerce計劃任務轉載于:https://www.cnblogs.com/chenjz/p/6293210.html

簡單談談js中的MVC

MVC是什么? MVC是一種架構模式,它將應用抽象為3個部分:模型(數據)、視圖、控制器(分發器)。 本文將用一個經典的例子todoList來展開(代碼在最后)。 一個事件發生的過程&a…

BTrace:Java開發人員工具箱中的隱藏寶石

這篇文章是關于BTrace的 ,我正在考慮將其作為Java開發人員的隱藏寶藏。 BTrace是用于Java平臺的安全,動態跟蹤工具。 BTrace可用于動態跟蹤正在運行的Java程序(類似于DTrace,適用于OpenSolaris應用程序和OS)。 不久&am…

python 圖片轉視頻ffmpeg_python圖片轉視頻(opencv),ffmpeg壓縮視頻

要注意:1. 圖片傳視頻要自己設置幀率和分辨率2.讀取圖片后分辨率要resize為和視頻分辨率一樣才可以3.寫完.avi視頻后視頻比較大,用ffmpeg將avi視頻壓縮為mp4import cv2from cv2 import VideoWriter, VideoWriter_fourcc, imread, resizeimport osfrom su…

門面模式

門面模式的定義 門面模式(Facade Pattern)也叫做外觀模式,是一種比較常用的封裝模式,其定義如 下: Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level interface tha…

Mysql數據庫申請

前段時間大部門下新成立了一個推廣百度OCR、文字識別、圖像識別等科技能力在金融領域應用的子部門。因為部門剛成立,基礎設施和人力都是欠缺的。當時分到我們部門的任務是抽調一個人做新部門主站前端開發工作。本來說的是只負責頁面的開發工作。當我參加過需求品審會…

Spring–添加SpringMVC –第2部分

在上一部分中,我們為經理和員工實現了控制器。 既然我們知道了解決方法,我們將做很少(但僅做很少)更復雜的事情–任務和時間表的控制器。 因此,讓我們從org.timesheet.web開始。 TaskController 。 首先創建一個類&…

php 正則分隔_探討PHP函數split()如何使用正則表達式切割字符串

對于初學者來說,掌握PHP中常用函數的用法,是其繼續學習的基礎。今天我們就為大家詳細介紹有關PHP函數split()的一些使用方法,希望大家能通過這篇文章介紹的內容增加自己的知識庫。說明array split ( string $pattern, string $string [, int …

通用的ProtostuffSerializer for Java

以前使用 protobuf或protostuff的時候覺得很麻煩,每個類都要單獨定制,于是封裝了一個類。 同事測試過,性能和壓縮率都很好,尤其是相比json的序列化。 需注意:只支持Pojo類(即需要有get/set方法)…