国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設(shè)計 > 正文

Java并發(fā)編程

2019-11-14 21:33:41
字體:
供稿:網(wǎng)友
java并發(fā)編程 - 不變性

這篇記錄一下保證并發(fā)安全性的策略之——不變性。(注意:是Immutable,不是Invariant!)

將一連串行為組織為一個原子操作以保證不變性條件,或者使用同步機制保證可見性,以防止讀到失效數(shù)據(jù)或者對象變?yōu)椴灰恢聽顟B(tài),這些問題都是因為共享了可變的數(shù)據(jù)。

如果我們能保證數(shù)據(jù)不可變,則這些復雜的問題就自然不用去考慮了。

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

說簡單也簡單,不可變對象只有一種狀態(tài),且由構(gòu)造器控制。

因此,判斷不可變對象的狀態(tài)變得特別簡單。

當我們共享一個可變對象,其狀態(tài)的改變行為都是難以預料的,尤其是作為參數(shù)傳給了可覆蓋的方法時,更糟糕的是這些client代碼都可以保留該對象的引用,也就是說狀態(tài)改變的時機也同樣難以預料。

相對于可變對象的共享,不可變對象的共享則簡單很多,而且?guī)缀醪挥每紤]弄一個快照。

于是我們現(xiàn)在有了一個新的問題:如何讓狀態(tài)不可變?

對于"不可變"這一說法無論是JLS還是什么地方都沒有明確的定義,但不可變絕對不僅僅是加個final修飾那么簡單,比如final修飾的field引用的是一個可變對象,而final保證的僅僅是引用的指向不會發(fā)生變化。

沒錯,不可變對象和不可變的對象引用是兩碼事

對于如何構(gòu)建一個不可變對象,我們有三個條件(雖然說是"條件",但并不是那么硬性的,可以算是某種建議):

  1. 對象創(chuàng)建后保證狀態(tài)不可變

  2. 對象的所有field都是final

  3. 創(chuàng)建期間沒有逸出自身引用,保證對象的創(chuàng)建正確。

關(guān)于上面三條,這里舉一個例子:

 public final class ThreeStooges {    PRivate final Set<String> stooges = new HashSet<String>();    public ThreeStooges() {        stooges.add("Moe");        stooges.add("Larry");        stooges.add("Curly");    }    public boolean isStooge(String name) {        return stooges.contains(name);    }    public String getStoogeNames() {        List<String> stooges = new Vector<String>();        stooges.add("Moe");        stooges.add("Larry");        stooges.add("Curly");        return stooges.toString();    }}

讓我們檢查一下是否滿足三個條件:

  1. 對象創(chuàng)建后保證狀態(tài)不可變,是否有變化? 我們首先是用private修飾了stooges,接著提供的兩個公有方法中第一個方法是返回boolean而第二個方法getStoogeNames中我們重新創(chuàng)建了一個stooges且保證相同的邏輯而不是直接引用stooges field。

  2. 對象的所有field都是final,很明顯,我們用了final進行描述以防止對象狀態(tài)在對象生命周期內(nèi)改變其引用。

  3. 創(chuàng)建期間沒有逸出自身引用,在stooges聲明時我們就指定了引用,并在構(gòu)造函數(shù)中將其初始化,不會有外來方法可以引用到該狀態(tài)并將其改編。

不得不說這個final修飾是關(guān)鍵。

通常我們對final關(guān)鍵字最直觀的印象是,如果一個用final修飾的對象引用的指向是不會改變的(發(fā)現(xiàn)這話怎么說都很難表達清楚,但是你懂的),但即使引用了可變的實例,就判斷狀態(tài)而言,加了final就可以簡化不少,分析基本不可變的對象總比分析完全可變的對象來得容易多了吧....

而final和synchronized關(guān)鍵字那樣也有多個語義,就是——能確保初始化過程的安全性,從而可以自由共享,不需要進行同步處理(這個同步處理不包括可見性)。

下面是一段用final(更確切地說應該是不可變性)保證了操作原子性(以保證可變性條件)一段例子。

某個Servlet接收參數(shù)后將參數(shù)傳入factor方法對其進行運算并將結(jié)果進行響應。

假設(shè)這個factor方法非常耗時,于是我們想出了一個方法暫時緩解這一狀況,即下一次請求的參數(shù)和上一次請求的參數(shù)相同則響應緩存中的結(jié)果。

也就是說每一次請求時我們多了一個步驟,也就是需要判斷請求的數(shù)字是否和緩存中的一樣,如果不同則重新計算,而這一段并不是原子操作,并發(fā)出現(xiàn)時會出現(xiàn)破壞可變性條件的情況。

而為了應對這個問題,我們可以將這一部分用synchronized保證其原子性,但這里使用另一種方式,使用不可變對象:

public class OneValueCache {    private final BigInteger lastNumber;    private final BigInteger[] lastFactors;    public OneValueCache(BigInteger i,                         BigInteger[] factors) {        lastNumber = i;        lastFactors = Arrays.copyOf(factors, factors.length);    }    public BigInteger[] getFactors(BigInteger i) {        if (lastNumber == null || !lastNumber.equals(i))            return null;        else            return Arrays.copyOf(lastFactors, lastFactors.length);    }}

這個不可變對象是如何設(shè)計的?

首先我們保證了所有狀態(tài)用final進行修飾并在唯一的構(gòu)造器中進行初始化,注意構(gòu)造器中對lastFactors進行初始化的那一段,我們用Arrays.copyOf保證了其正確構(gòu)造,也就是防止逸出。

然后是唯一一個公有方法,這個方法要返回的正是我們計算好的factors,但我們不能直接返回factors,也是為了防止逸出,我們使用了Arrays.copyOf。

下面是使用緩存的Servlet,整個對象只有一個field就是cache,我們用volatile修飾以保證并發(fā)時的可見性,即線程A改變了引用時線程B可以立即看到新的緩存。

public class VolatileCachedFactorizer extends GenericServlet implements Servlet {    private volatile OneValueCache cache = new OneValueCache(null, null);    public void service(ServletRequest req, ServletResponse resp) {        BigInteger i = extractFromRequest(req);        BigInteger[] factors = cache.getFactors(i);        if (factors == null) {            factors = factor(i);            cache = new OneValueCache(i, factors);        }        encodeIntoResponse(resp, factors);    }    void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) {    }    BigInteger extractFromRequest(ServletRequest req) {        return new BigInteger("7");    }    BigInteger[] factor(BigInteger i) {        return new BigInteger[]{i};    }}

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 万荣县| 体育| 响水县| 梧州市| 昌江| 钦州市| 永吉县| 隆尧县| 信丰县| 全椒县| 青神县| 兴文县| 台东县| 友谊县| 尖扎县| 西畴县| 石门县| 万年县| 化隆| 南昌市| 孟村| 潢川县| 云和县| 邵阳县| 普定县| 那曲县| 周至县| 台南市| 油尖旺区| 乌拉特后旗| 宁河县| 大港区| 绵阳市| 仁寿县| 巴林右旗| 左贡县| 皋兰县| 香港| 岐山县| 鸡西市| 隆尧县|