欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java并發(fā)編程之對(duì)象的組合

 更新時(shí)間:2022年04月07日 14:50:30   作者:萬(wàn)貓學(xué)社  
這篇文章主要介紹了Java并發(fā)編程之對(duì)象的組合,文章基于Java的相關(guān)資料展開(kāi)主題內(nèi)容,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

1. 設(shè)計(jì)線(xiàn)程安全的類(lèi)

在設(shè)計(jì)線(xiàn)程安全類(lèi)的過(guò)程中,需要包含以下三個(gè)基本要素:

  • 找出構(gòu)成對(duì)象狀態(tài)的所有變量。
  • 找出約束變量的不變性條件。
  • 建立對(duì)象狀態(tài)的并發(fā)訪問(wèn)管理策略。

1.1 收集同步需求

在很多類(lèi)中都定義了一些不可變條件,用于判斷狀態(tài)是否有效。比如:計(jì)數(shù)器的取值范圍上存在一個(gè)限制,就是不能是負(fù)值。在操作中還會(huì)包含一些后驗(yàn)條件來(lái)判斷狀態(tài)遷移是否有有效。比如:計(jì)數(shù)器的當(dāng)前狀態(tài)為8,那么下一個(gè)有效狀態(tài)只能是9。由于不變性條件和后驗(yàn)條件在狀態(tài)及狀態(tài)轉(zhuǎn)換上施加了各種約束,因此就需要額外的同步和封裝。

1.2 依賴(lài)狀態(tài)的操作

在某些對(duì)象的方法中還包含一些基于狀態(tài)的先驗(yàn)條件。比如:不能從空隊(duì)列中移除一個(gè)元素,在刪除元素前,隊(duì)列必須處于非空狀態(tài)。如果在某個(gè)操作中包含有基于狀態(tài)的先驗(yàn)條件,那么這個(gè)操作就稱(chēng)為依賴(lài)狀態(tài)的操作。

1.3 狀態(tài)的所有權(quán)

對(duì)象封裝了它擁有的狀態(tài),那么它就擁有封裝狀態(tài)的所有權(quán)。狀態(tài)的所有者將決定采用何種加鎖協(xié)議來(lái)維持狀態(tài)的完整性。所有權(quán)意味著控制權(quán)。如果發(fā)布了某個(gè)可變對(duì)象的引用,那么就不再擁有獨(dú)占的控制權(quán),最多是共享控制權(quán)。為了防止多個(gè)線(xiàn)程在并發(fā)訪問(wèn)同一個(gè)對(duì)象時(shí)產(chǎn)生相互干擾,這些對(duì)象應(yīng)該要么是線(xiàn)程安全的對(duì)象,要么是事實(shí)不可變的對(duì)象,或者用同一個(gè)鎖保護(hù)的對(duì)象。

2. 實(shí)例封閉

將數(shù)據(jù)封裝在對(duì)象內(nèi)部,可以將數(shù)據(jù)的訪問(wèn)限制在對(duì)象的方法上,從而更容易確保線(xiàn)程在訪問(wèn)數(shù)據(jù)是總能持有正確的鎖。被封閉對(duì)象一定不能超過(guò)它們既定的作用域。對(duì)象可以封閉在類(lèi)的一個(gè)實(shí)例中,比如:作為類(lèi)的一個(gè)私有成員;或者封閉在某個(gè)作用域內(nèi),比如:作為一個(gè)局部變量;或者封閉在線(xiàn)程中,比如:在某個(gè)線(xiàn)程中將對(duì)象從一個(gè)方法傳遞到另一個(gè)方法,而不是在多個(gè)線(xiàn)程之間共享該對(duì)象。

封閉機(jī)制更易于構(gòu)造線(xiàn)程安全的類(lèi),因?yàn)楫?dāng)封閉類(lèi)的狀態(tài)時(shí),在分析類(lèi)的線(xiàn)程安全性時(shí)就無(wú)須檢測(cè)整個(gè)程序。

2.1 Java監(jiān)視器模式

遵循Java監(jiān)視器模式的對(duì)象會(huì)把對(duì)象的所有可變狀態(tài)都封裝起來(lái),并由對(duì)象自己的內(nèi)置鎖來(lái)保護(hù)。示例:一個(gè)用于調(diào)度車(chē)輛的“車(chē)輛跟蹤器”,每臺(tái)車(chē)都由一個(gè)String對(duì)象來(lái)標(biāo)識(shí),并且擁有一個(gè)對(duì)應(yīng)的位置坐標(biāo)(x,y)。創(chuàng)建一個(gè)追蹤器類(lèi)用來(lái)封裝所有車(chē)輛的標(biāo)識(shí)和位置,該類(lèi)由多個(gè)線(xiàn)程(讀取操作和更新操作)共享。

@NotThreadSafe
public class MutablePoint {
    public int x, y;

    public MutablePoint() {
        x = 0;
        y = 0;
    }

    public MutablePoint(MutablePoint p) {
        this.x = p.x;
        this.y = p.y;
    }
}
@ThreadSafe
public class MonitorVehicleTracker {
    @GuardedBy("this") private final Map<String, MutablePoint> locations;

    public MonitorVehicleTracker(Map<String, MutablePoint> locations) {
        this.locations = deepCopy(locations);
    }

    public synchronized Map<String, MutablePoint> getLocations() {
        return deepCopy(locations);
    }

    public synchronized MutablePoint getLocation(String id) {
        MutablePoint loc = locations.get(id);
        return loc == null ? null : new MutablePoint(loc);
    }

    public synchronized void setLocation(String id, int x, int y) {
        MutablePoint loc = locations.get(id);
        if (loc == null)
            throw new IllegalArgumentException("No such ID: " + id);
        loc.x = x;
        loc.y = y;
    }

    private static Map<String, MutablePoint> deepCopy(Map<String, MutablePoint> m) {
        Map<String, MutablePoint> result = new HashMap<String, MutablePoint>();

        for (String id : m.keySet())
            result.put(id, new MutablePoint(m.get(id)));

        return Collections.unmodifiableMap(result);
    }
}

雖然類(lèi)MutablePoint不是線(xiàn)程安全的,但是追蹤器類(lèi)是線(xiàn)程安全的。它所包含的Map對(duì)象和可變的Point對(duì)象都未曾發(fā)布。當(dāng)需要回去車(chē)輛的位置時(shí),通過(guò)拷貝構(gòu)造函數(shù)或deepCopy方法復(fù)制正確的值,從而生成一個(gè)新的Map對(duì)象。車(chē)輛少是,這并不存在性能問(wèn),單在車(chē)輛數(shù)量非常大的情況下將極大地降低性能。

3. 線(xiàn)程安全性的委托

大多數(shù)對(duì)象都是組合對(duì)象。當(dāng)從頭開(kāi)始構(gòu)建一個(gè)類(lèi),或者將多個(gè)非線(xiàn)程安全的類(lèi)組合為一個(gè)類(lèi)時(shí),Java監(jiān)視器模式是非常有用的。但是,如果類(lèi)中的各個(gè)組件都已經(jīng)是線(xiàn)程安全的,那么實(shí)現(xiàn)線(xiàn)程安全就應(yīng)視情況而定。

3.1 基于委托的車(chē)輛追蹤器

保存車(chē)輛位置信息的Map類(lèi),使用線(xiàn)程安全的ConcurrentMap類(lèi)代替。

使用不可變的Point類(lèi)來(lái)代替MutablePoint類(lèi):

public class Point {
    public final int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

在DelegatingVehicleTracker中沒(méi)有使用顯式的同步,所有對(duì)狀態(tài)的訪問(wèn)都由ConcurrentHashMap來(lái)管理:

@ThreadSafe
public class DelegatingVehicleTracker {
    private final ConcurrentMap<String, Point> locations;
    private final Map<String, Point> unmodifiableMap;

    public DelegatingVehicleTracker(Map<String, Point> points) {
        locations = new ConcurrentHashMap<String, Point>(points);
        unmodifiableMap = Collections.unmodifiableMap(locations);
    }

    public Map<String, Point> getLocations() {
        return unmodifiableMap;
    }

    public Point getLocation(String id) {
        return locations.get(id);
    }

    public void setLocation(String id, int x, int y) {
        if (locations.replace(id, new Point(x, y)) == null)
            throw new IllegalArgumentException("invalid vehicle name: " + id);
    }
}

在使用監(jiān)視器模式的車(chē)輛追蹤器中返回的是車(chē)輛位置的快照,而在使用委托的車(chē)輛追蹤器中返回的是一個(gè)不可修改卻實(shí)時(shí)的車(chē)輛位置信息。

3.2 獨(dú)立的狀態(tài)變量

我們還可以將線(xiàn)程安全性委托給多個(gè)狀態(tài)變量,只要這些變量是彼此獨(dú)立的,即組合而成的類(lèi)并不會(huì)在其包含的多個(gè)狀態(tài)變量上增加任何不變形條件。

public class VisualComponent {
    private final List<KeyListener> keyListeners
            = new CopyOnWriteArrayList<KeyListener>();
    private final List<MouseListener> mouseListeners
            = new CopyOnWriteArrayList<MouseListener>();

    public void addKeyListener(KeyListener listener) {
        keyListeners.add(listener);
    }

    public void addMouseListener(MouseListener listener) {
        mouseListeners.add(listener);
    }

    public void removeKeyListener(KeyListener listener) {
        keyListeners.remove(listener);
    }

    public void removeMouseListener(MouseListener listener) {
        mouseListeners.remove(listener);
    }
}

VisualComponent類(lèi)是一個(gè)圖形組件,允許客戶(hù)程序注冊(cè)監(jiān)控鼠標(biāo)和鍵盤(pán)等事件的監(jiān)聽(tīng)器。在鼠標(biāo)事件監(jiān)聽(tīng)器和鍵盤(pán)事件監(jiān)聽(tīng)器之間不存在任何關(guān)聯(lián),二者是彼此獨(dú)立的,因此VisualComponent可以將其線(xiàn)程安全性委托給這兩個(gè)線(xiàn)程安全的監(jiān)聽(tīng)器列表。

如果一個(gè)類(lèi)是由多個(gè)獨(dú)立且線(xiàn)程安全的狀態(tài)變量組成,并且在所有的操作中都不包含無(wú)效狀態(tài)轉(zhuǎn)換,那么可以將線(xiàn)程安全性委托給底層的狀態(tài)變量。

3.3 發(fā)布底層的狀態(tài)變量

如果一個(gè)狀態(tài)變量是線(xiàn)程安全的,并且沒(méi)有任何不變性條件來(lái)約束它的值,在變量的操作上也不存在任何不允許的狀態(tài)轉(zhuǎn)換,那么就可以安全地發(fā)布這個(gè)變量。我們來(lái)修改之前的車(chē)輛追蹤器的代碼,使其發(fā)布底層的可變狀態(tài)。

首先寫(xiě)一個(gè)可變并且線(xiàn)程安全的Point類(lèi):

public class SafePoint {
    @GuardedBy("this") private int x, y;

    private SafePoint(int[] a) {
        this(a[0], a[1]);
    }

    public SafePoint(SafePoint p) {
        this(p.get());
    }

    public SafePoint(int x, int y) {
        this.set(x, y);
    }

    public synchronized int[] get() {
        return new int[]{x, y};
    }

    public synchronized void set(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

再修改追蹤器的代碼:

public class PublishingVehicleTracker {
    private final Map<String, SafePoint> locations;
    private final Map<String, SafePoint> unmodifiableMap;

    public PublishingVehicleTracker(Map<String, SafePoint> locations) {
        this.locations = new ConcurrentHashMap<String, SafePoint>(locations);
        this.unmodifiableMap = Collections.unmodifiableMap(this.locations);
    }

    public Map<String, SafePoint> getLocations() {
        return unmodifiableMap;
    }

    public SafePoint getLocation(String id) {
        return locations.get(id);
    }

    public void setLocation(String id, int x, int y) {
        if (!locations.containsKey(id))
            throw new IllegalArgumentException("invalid vehicle name: " + id);
        locations.get(id).set(x, y);
    }
}

PublishingVehicleTracker中,其線(xiàn)程安全性委托給底層的ConcurrentHashMap,只是Map中的元素是線(xiàn)程安全的可變的。getLocations方法返回底層Map對(duì)象的一個(gè)不可變副本,調(diào)用者不能增加或刪除車(chē)輛,但可以通過(guò)修改返回Map中的SafePoint值來(lái)修改車(chē)輛的問(wèn)題。

到此這篇關(guān)于Java并發(fā)編程之對(duì)象的組合的文章就介紹到這了,更多相關(guān)Java對(duì)象的組合內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論