亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

如何使用 Java8 實現(xiàn)觀察者模式?(下)

afishhhhh / 604人閱讀

摘要:線程安全的實現(xiàn)前面章節(jié)介紹了在現(xiàn)代環(huán)境下的實現(xiàn)觀察者模式,雖然簡單但很完整,但這一實現(xiàn)忽略了一個關(guān)鍵性問題線程安全。截止目前實現(xiàn)了線程安全,在接下來的章節(jié)中將介紹提取主題的邏輯并將其類封裝為可重復(fù)代碼單元的方式優(yōu)缺點。

【編者按】本文作者是 BAE 系統(tǒng)公司的軟件工程師 Justin Albano。在本篇文章中,作者通過在 Java8 環(huán)境下實現(xiàn)觀察者模式的實例,進(jìn)一步介紹了什么是觀察者模式、專業(yè)化及其命名規(guī)則,供大家參考學(xué)習(xí)。本文系國內(nèi) ITOM 管理平臺 OneAPM 工程師編譯整理。

線程安全的實現(xiàn)

前面章節(jié)介紹了在現(xiàn)代Java環(huán)境下的實現(xiàn)觀察者模式,雖然簡單但很完整,但這一實現(xiàn)忽略了一個關(guān)鍵性問題:線程安全。大多數(shù)開放的Java應(yīng)用都是多線程的,而且觀察者模式也多用于多線程或異步系統(tǒng)。例如,如果外部服務(wù)更新其數(shù)據(jù)庫,那么應(yīng)用也會異步地收到消息,然后用觀察者模式通知內(nèi)部組件更新,而不是內(nèi)部組件直接注冊監(jiān)聽外部服務(wù)。

觀察者模式的線程安全主要集中在模式的主體上,因為修改注冊監(jiān)聽器集合時很可能發(fā)生線程沖突,比如,一個線程試圖添加一個新的監(jiān)聽器,而另一線程又試圖添加一個新的animal對象,這將觸發(fā)對所有注冊監(jiān)聽器的通知。鑒于先后順序,在已注冊的監(jiān)聽器收到新增動物的通知前,第一個線程可能已經(jīng)完成也可能尚未完成新監(jiān)聽器的注冊。這是一個經(jīng)典的線程資源競爭案例,正是這一現(xiàn)象告訴開發(fā)者們需要一個機(jī)制來保證線程安全。

這一問題的最簡單的解決方案是:所有訪問或修改注冊監(jiān)聽器list的操作都須遵循Java的同步機(jī)制,比如:

public synchronized AnimalAddedListener registerAnimalAddedListener (AnimalAddedListener listener) { /*...*/ }
public synchronized void unregisterAnimalAddedListener (AnimalAddedListener listener) { /*...*/ }
public synchronized void notifyAnimalAddedListeners (Animal animal) { /*...*/ }

這樣一來,同一時刻只有一個線程可以修改或訪問已注冊的監(jiān)聽器列表,可以成功地避免資源競爭問題,但是新問題又出現(xiàn)了,這樣的約束太過嚴(yán)格(synchronized關(guān)鍵字和Java并發(fā)模型的更多信息,請參閱官方網(wǎng)頁)。通過方法同步,可以時刻觀測對監(jiān)聽器list的并發(fā)訪問,注冊和撤銷監(jiān)聽器對監(jiān)聽器list而言是寫操作,而通知監(jiān)聽器訪問監(jiān)聽器list是只讀操作。由于通過通知訪問是讀操作,因此是可以多個通知操作同時進(jìn)行的。

因此,只要沒有監(jiān)聽器注冊或撤銷注冊,任意多的并發(fā)通知都可以同時執(zhí)行,而不會引發(fā)對注冊的監(jiān)聽器列表的資源爭奪。當(dāng)然,其他情況下的資源爭奪現(xiàn)象存在已久,為了解決這一問題,設(shè)計了ReadWriteLock用以分開管理讀寫操作的資源鎖定。Zoo類的線程安全ThreadSafeZoo實現(xiàn)代碼如下:

public class ThreadSafeZoo {
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    protected final Lock readLock = readWriteLock.readLock();
    protected final Lock writeLock = readWriteLock.writeLock();
    private List animals = new ArrayList<>();
    private List listeners = new ArrayList<>();
    public void addAnimal (Animal animal) {
        // Add the animal to the list of animals
        this.animals.add(animal);
        // Notify the list of registered listeners
        this.notifyAnimalAddedListeners(animal);
    }
    public AnimalAddedListener registerAnimalAddedListener (AnimalAddedListener listener) {
        // Lock the list of listeners for writing
        this.writeLock.lock();
        try {
            // Add the listener to the list of registered listeners
            this.listeners.add(listener);
        }
        finally {
            // Unlock the writer lock
            this.writeLock.unlock();
        }
        return listener;
    }
    public void unregisterAnimalAddedListener (AnimalAddedListener listener) {
        // Lock the list of listeners for writing
        this.writeLock.lock();
        try {
            // Remove the listener from the list of the registered listeners
            this.listeners.remove(listener);
        }
        finally {
            // Unlock the writer lock
            this.writeLock.unlock();
        }
    }
    public void notifyAnimalAddedListeners (Animal animal) {
        // Lock the list of listeners for reading
        this.readLock.lock();
        try {
            // Notify each of the listeners in the list of registered listeners
            this.listeners.forEach(listener -> listener.updateAnimalAdded(animal));
        }
        finally {
            // Unlock the reader lock
            this.readLock.unlock();
        }
    }
}

通過這樣部署,Subject的實現(xiàn)能確保線程安全并且多個線程可以同時發(fā)布通知。但盡管如此,依舊存在兩個不容忽略的資源競爭問題:

對每個監(jiān)聽器的并發(fā)訪問。多個線程可以同時通知監(jiān)聽器要新增動物了,這意味著一個監(jiān)聽器可能會同時被多個線程同時調(diào)用。

對animal list的并發(fā)訪問。多個線程可能會同時向animal list添加對象,如果通知的先后順序存在影響,那就可能導(dǎo)致資源競爭,這就需要一個并發(fā)操作處理機(jī)制來避免這一問題。如果注冊的監(jiān)聽器列表在收到通知添加animal2后,又收到通知添加animal1,此時就會產(chǎn)生資源競爭。但是如果animal1和animal2的添加由不同的線程執(zhí)行,也是有可能在animal2前完成對animal1添加操作,具體來說就是線程1在通知監(jiān)聽器前添加animal1并鎖定模塊,線程2添加animal2并通知監(jiān)聽器,然后線程1通知監(jiān)聽器animal1已經(jīng)添加。雖然在不考慮先后順序時,可以忽略資源競爭,但問題是真實存在的。

對監(jiān)聽器的并發(fā)訪問

并發(fā)訪問監(jiān)聽器可以通過保證監(jiān)聽器的線程安全來實現(xiàn)。秉承著類的“責(zé)任自負(fù)”精神,監(jiān)聽器有“義務(wù)”確保自身的線程安全。例如,對于前面計數(shù)的監(jiān)聽器,多線程的遞增或遞減動物數(shù)量可能導(dǎo)致線程安全問題,要避免這一問題,動物數(shù)的計算必須是原子操作(原子變量或方法同步),具體解決代碼如下:

public class ThreadSafeCountingAnimalAddedListener implements AnimalAddedListener {
    private static AtomicLong animalsAddedCount = new AtomicLong(0);
    @Override
    public void updateAnimalAdded (Animal animal) {
        // Increment the number of animals
        animalsAddedCount.incrementAndGet();
        // Print the number of animals
        System.out.println("Total animals added: " + animalsAddedCount);
    }
}

方法同步解決方案代碼如下:

public class CountingAnimalAddedListener implements AnimalAddedListener {
    private static int animalsAddedCount = 0;
    @Override
    public synchronized void updateAnimalAdded (Animal animal) {
        // Increment the number of animals
        animalsAddedCount++;
        // Print the number of animals
        System.out.println("Total animals added: " + animalsAddedCount);
    }
}

要強(qiáng)調(diào)的是監(jiān)聽器應(yīng)該保證自身的線程安全,subject需要理解監(jiān)聽器的內(nèi)部邏輯,而不是簡單確保對監(jiān)聽器的訪問和修改的線程安全。否則,如果多個subject共用同一個監(jiān)聽器,那每個subject類都要重寫一遍線程安全的代碼,顯然這樣的代碼不夠簡潔,因此需要在監(jiān)聽器類內(nèi)實現(xiàn)線程安全。

監(jiān)聽器的有序通知

當(dāng)要求監(jiān)聽器有序執(zhí)行時,讀寫鎖就不能滿足需求了,而需要引入一個新的機(jī)制,可以保證notify函數(shù)的調(diào)用順序和animal添加到zoo的順序一致。有人嘗試過用方法同步來實現(xiàn),然而根據(jù)Oracle文檔中的方法同步介紹,可知方法同步并不提供操作執(zhí)行的順序管理。它只是保證原子操作,也就是說操作不會被打斷,并不能保證先來先執(zhí)行(FIFO)的線程順序。ReentrantReadWriteLock可以實現(xiàn)這樣的執(zhí)行順序,代碼如下:

public class OrderedThreadSafeZoo {
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    protected final Lock readLock = readWriteLock.readLock();
    protected final Lock writeLock = readWriteLock.writeLock();
    private List animals = new ArrayList<>();
    private List listeners = new ArrayList<>();
    public void addAnimal (Animal animal) {
        // Add the animal to the list of animals
        this.animals.add(animal);
        // Notify the list of registered listeners
        this.notifyAnimalAddedListeners(animal);
    }
    public AnimalAddedListener registerAnimalAddedListener (AnimalAddedListener listener) {
        // Lock the list of listeners for writing
        this.writeLock.lock();
        try {
            // Add the listener to the list of registered listeners
            this.listeners.add(listener);
        }
        finally {
            // Unlock the writer lock
            this.writeLock.unlock();
        }
        return listener;
    }
    public void unregisterAnimalAddedListener (AnimalAddedListener listener) {
        // Lock the list of listeners for writing
        this.writeLock.lock();
        try {
            // Remove the listener from the list of the registered listeners
            this.listeners.remove(listener);
        }
        finally {
            // Unlock the writer lock
            this.writeLock.unlock();
        }
    }
    public void notifyAnimalAddedListeners (Animal animal) {
        // Lock the list of listeners for reading
        this.readLock.lock();
        try {
            // Notify each of the listeners in the list of registered listeners
            this.listeners.forEach(listener -> listener.updateAnimalAdded(animal));
        }
        finally {
            // Unlock the reader lock
            this.readLock.unlock();
        }
    }
}

這樣的實現(xiàn)方式,register, unregister和notify函數(shù)將按照先進(jìn)先出(FIFO)的順序獲得讀寫鎖權(quán)限。例如,線程1注冊一個監(jiān)聽器,線程2在開始執(zhí)行注冊操作后試圖通知已注冊的監(jiān)聽器,線程3在線程2等待只讀鎖的時候也試圖通知已注冊的監(jiān)聽器,采用fair-ordering方式,線程1先完成注冊操作,然后線程2可以通知監(jiān)聽器,最后線程3通知監(jiān)聽器。這樣保證了action的執(zhí)行順序和開始順序一致。

如果采用方法同步,雖然線程2先排隊等待占用資源,線程3仍可能比線程2先獲得資源鎖,而且不能保證線程2比線程3先通知監(jiān)聽器。問題的關(guān)鍵所在:fair-ordering方式可以保證線程按照申請資源的順序執(zhí)行。讀寫鎖的順序機(jī)制很復(fù)雜,應(yīng)參照ReentrantReadWriteLock的官方文檔以確保鎖的邏輯足夠解決問題。

截止目前實現(xiàn)了線程安全,在接下來的章節(jié)中將介紹提取主題的邏輯并將其mixin類封裝為可重復(fù)代碼單元的方式優(yōu)缺點。

主題邏輯封裝到Mixin類

把上述的觀察者模式設(shè)計實現(xiàn)封裝到目標(biāo)的mixin類中很具吸引力。通常來說,觀察者模式中的觀察者包含已注冊的監(jiān)聽器的集合;負(fù)責(zé)注冊新的監(jiān)聽器的register函數(shù);負(fù)責(zé)撤銷注冊的unregister函數(shù)和負(fù)責(zé)通知監(jiān)聽器的notify函數(shù)。對于上述的動物園的例子,zoo類除動物列表是問題所需外,其他所有操作都是為了實現(xiàn)主題的邏輯。

Mixin類的案例如下所示,需要說明的是為使代碼更為簡潔,此處去掉關(guān)于線程安全的代碼:

public abstract class ObservableSubjectMixin {
    private List listeners = new ArrayList<>();
    public ListenerType registerListener (ListenerType listener) {
        // Add the listener to the list of registered listeners
        this.listeners.add(listener);
        return listener;
    }
    public void unregisterAnimalAddedListener (ListenerType listener) {
        // Remove the listener from the list of the registered listeners
        this.listeners.remove(listener);
    }
    public void notifyListeners (Consumer algorithm) {
        // Execute some function on each of the listeners
        this.listeners.forEach(algorithm);
    }
}

正因為沒有提供正在注冊的監(jiān)聽器類型的接口信息,不能直接通知某個特定的監(jiān)聽器,所以正需要保證通知功能的通用性,允許客戶端添加一些功能,如接受泛型參數(shù)類型的參數(shù)匹配,以適用于每個監(jiān)聽器,具體實現(xiàn)代碼如下:

public class ZooUsingMixin extends ObservableSubjectMixin {
    private List animals = new ArrayList<>();
    public void addAnimal (Animal animal) {
        // Add the animal to the list of animals
        this.animals.add(animal);
        // Notify the list of registered listeners
        this.notifyListeners((listener) -> listener.updateAnimalAdded(animal));
    }
}

Mixin類技術(shù)的最大優(yōu)勢是把觀察者模式的Subject封裝到一個可重復(fù)調(diào)用的類中,而不是在每個subject類中都重復(fù)寫這些邏輯。此外,這一方法使得zoo類的實現(xiàn)更為簡潔,只需要存儲動物信息,而不用再考慮如何存儲和通知監(jiān)聽器。

然而,使用mixin類并非只有優(yōu)點。比如,如果要存儲多個類型的監(jiān)聽器怎么辦?例如,還需要存儲監(jiān)聽器類型AnimalRemovedListener。mixin類是抽象類,Java中不能同時繼承多個抽象類,而且mixin類不能改用接口實現(xiàn),這是因為接口不包含state,而觀察者模式中state需要用來保存已經(jīng)注冊的監(jiān)聽器列表。

其中的一個解決方案是創(chuàng)建一個動物增加和減少時都會通知的監(jiān)聽器類型ZooListener,代碼如下所示:

public interface ZooListener {
    public void onAnimalAdded (Animal animal);
    public void onAnimalRemoved (Animal animal);
}

這樣就可以使用該接口實現(xiàn)利用一個監(jiān)聽器類型對zoo狀態(tài)各種變化的監(jiān)聽了:

public class ZooUsingMixin extends ObservableSubjectMixin {
    private List animals = new ArrayList<>();
    public void addAnimal (Animal animal) {
        // Add the animal to the list of animals
        this.animals.add(animal);
        // Notify the list of registered listeners
        this.notifyListeners((listener) -> listener.onAnimalAdded(animal));
    }
    public void removeAnimal (Animal animal) {
        // Remove the animal from the list of animals
        this.animals.remove(animal);
        // Notify the list of registered listeners
        this.notifyListeners((listener) -> listener.onAnimalRemoved(animal));
    }
}

將多個監(jiān)聽器類型合并到一個監(jiān)聽器接口中確實解決了上面提到的問題,但仍舊存在不足之處,接下來的章節(jié)會詳細(xì)討論。

Multi-Method監(jiān)聽器和適配器

在上述方法,監(jiān)聽器的接口中實現(xiàn)的包含太多函數(shù),接口就過于冗長,例如,Swing MouseListener就包含5個必要的函數(shù)。盡管可能只會用到其中一個,但是只要用到鼠標(biāo)點擊事件就必須要添加這5個函數(shù),更多可能是用空函數(shù)體來實現(xiàn)剩下的函數(shù),這無疑會給代碼帶來不必要的混亂。

其中一種解決方案是創(chuàng)建適配器(概念來自GoF提出的適配器模式),適配器中以抽象函數(shù)的形式實現(xiàn)監(jiān)聽器接口的操作,供具體監(jiān)聽器類繼承。這樣一來,具體監(jiān)聽器類就可以選擇其需要的函數(shù),對adapter不需要的函數(shù)采用默認(rèn)操作即可。例如上面例子中的ZooListener類,創(chuàng)建ZooAdapter(Adapter的命名規(guī)則與監(jiān)聽器一致,只需要把類名中的Listener改為Adapter即可),代碼如下:

public class ZooAdapter implements ZooListener {
    @Override
    public void onAnimalAdded (Animal animal) {}
    @Override
    public void onAnimalRemoved (Animal animal) {}
}

乍一看,這個適配器類微不足道,然而它所帶來的便利卻是不可小覷的。比如對于下面的具體類,只需選擇對其實現(xiàn)有用的函數(shù)即可:

public class NamePrinterZooAdapter extends ZooAdapter {
    @Override
    public void onAnimalAdded (Animal animal) {
        // Print the name of the animal that was added
        System.out.println("Added animal named " + animal.getName());
    }
}

有兩種替代方案同樣可以實現(xiàn)適配器類的功能:一是使用默認(rèn)函數(shù);二是把監(jiān)聽器接口和適配器類合并到一個具體類中。默認(rèn)函數(shù)是Java8新提出的,在接口中允許開發(fā)者提供默認(rèn)(防御)的實現(xiàn)方法。

Java庫的這一更新主要是方便開發(fā)者在不改變老版本代碼的情況下,實現(xiàn)程序擴(kuò)展,因此應(yīng)該慎用這個方法。部分開發(fā)者多次使用后,會感覺這樣寫的代碼不夠?qū)I(yè),而又有開發(fā)者認(rèn)為這是Java8的特色,不管怎樣,需要明白這個技術(shù)提出的初衷是什么,再結(jié)合具體問題決定是否要用。使用默認(rèn)函數(shù)實現(xiàn)的ZooListener接口代碼如下示:

public interface ZooListener {
    default public void onAnimalAdded (Animal animal) {}
    default public void onAnimalRemoved (Animal animal) {}
}

通過使用默認(rèn)函數(shù),實現(xiàn)該接口的具體類,無需在接口中實現(xiàn)全部函數(shù),而是選擇性實現(xiàn)所需函數(shù)。雖然這是接口膨脹問題一個較為簡潔的解決方案,開發(fā)者在使用時還應(yīng)多加注意。

第二種方案是簡化觀察者模式,省略了監(jiān)聽器接口,而是用具體類實現(xiàn)監(jiān)聽器的功能。比如ZooListener接口就變成了下面這樣:

public class ZooListener {
    public void onAnimalAdded (Animal animal) {}
    public void onAnimalRemoved (Animal animal) {}
}

這一方案簡化了觀察者模式的層次結(jié)構(gòu),但它并非適用于所有情況,因為如果把監(jiān)聽器接口合并到具體類中,具體監(jiān)聽器就不可以實現(xiàn)多個監(jiān)聽接口了。例如,如果AnimalAddedListener和AnimalRemovedListener接口寫在同一個具體類中,那么多帶帶一個具體監(jiān)聽器就不可以同時實現(xiàn)這兩個接口了。此外,監(jiān)聽器接口的意圖比具體類更顯而易見,很顯然前者就是為其他類提供接口,但后者就并非那么明顯了。

如果沒有合適的文檔說明,開發(fā)者并不會知道已經(jīng)有一個類扮演著接口的角色,實現(xiàn)了其對應(yīng)的所有函數(shù)。此外,類名不包含adapter,因為類并不適配于某一個接口,因此類名并沒有特別暗示此意圖。綜上所述,特定問題需要選擇特定的方法,并沒有哪個方法是萬能的。

在開始下一章前,需要特別提一下,適配器在觀察模式中很常見,尤其是在老版本的Java代碼中。Swing API正是以適配器為基礎(chǔ)實現(xiàn)的,正如很多老應(yīng)用在Java5和Java6中的觀察者模式中所使用的那樣。zoo案例中的監(jiān)聽器或許并不需要適配器,但需要了解適配器提出的目的以及其應(yīng)用,因為我們可以在現(xiàn)有的代碼中對其進(jìn)行使用。下面的章節(jié),將會介紹時間復(fù)雜的監(jiān)聽器,該類監(jiān)聽器可能會執(zhí)行耗時的運(yùn)算或進(jìn)行異步調(diào)用,不能立即給出返回值。

Complex & Blocking監(jiān)聽器

關(guān)于觀察者模式的一個假設(shè)是:執(zhí)行一個函數(shù)時,一系列監(jiān)聽器會被調(diào)用,但假定這一過程對調(diào)用者而言是完全透明的。例如,客戶端代碼在Zoo中添加animal時,在返回添加成功之前,并不知道會調(diào)用一系列監(jiān)聽器。如果監(jiān)聽器的執(zhí)行需要時間較長(其時間受監(jiān)聽器的數(shù)量、每個監(jiān)聽器執(zhí)行時間影響),那么客戶端代碼將會感知這一簡單增加動物操作的時間副作用。

本文不能面面俱到的討論這個話題,下面幾條是開發(fā)者調(diào)用復(fù)雜的監(jiān)聽器時應(yīng)該注意的事項:

監(jiān)聽器啟動新線程。新線程啟動后,在新線程中執(zhí)行監(jiān)聽器邏輯的同時,返回監(jiān)聽器函數(shù)的處理結(jié)果,并運(yùn)行其他監(jiān)聽器執(zhí)行。

Subject啟動新線程。與傳統(tǒng)的線性迭代已注冊的監(jiān)聽器列表不同,Subject的notify函數(shù)重啟一個新的線程,然后在新線程中迭代監(jiān)聽器列表。這樣使得notify函數(shù)在執(zhí)行其他監(jiān)聽器操作的同時可以輸出其返回值。需要注意的是需要一個線程安全機(jī)制來確保監(jiān)聽器列表不會進(jìn)行并發(fā)修改。

隊列化監(jiān)聽器調(diào)用并采用一組線程執(zhí)行監(jiān)聽功能。將監(jiān)聽器操作封裝在一些函數(shù)中并隊列化這些函數(shù),而非簡單的迭代調(diào)用監(jiān)聽器列表。這些監(jiān)聽器存儲到隊列中后,線程就可以從隊列中彈出單個元素并執(zhí)行其監(jiān)聽邏輯。這類似于生產(chǎn)者-消費(fèi)者問題,notify過程產(chǎn)生可執(zhí)行函數(shù)隊列,然后線程依次從隊列中取出并執(zhí)行這些函數(shù),函數(shù)需要存儲被創(chuàng)建的時間而非執(zhí)行的時間供監(jiān)聽器函數(shù)調(diào)用。例如,監(jiān)聽器被調(diào)用時創(chuàng)建的函數(shù),那么該函數(shù)就需要存儲該時間點,這一功能類似于Java中的如下操作:

public class AnimalAddedFunctor {
    private final AnimalAddedListener listener;
    private final Animal parameter;
    public AnimalAddedFunctor (AnimalAddedListener listener, Animal parameter) {
        this.listener = listener;
        this.parameter = parameter;
    }
    public void execute () {
        // Execute the listener with the parameter provided during creation
        this.listener.updateAnimalAdded(this.parameter);
    }
}

函數(shù)創(chuàng)建并保存在隊列中,可以隨時調(diào)用,這樣一來就無需在遍歷監(jiān)聽器列表時立即執(zhí)行其對應(yīng)操作了。一旦每個激活監(jiān)聽器的函數(shù)都壓入隊列中,“消費(fèi)者線程”就會給客戶端代碼返回操作權(quán)。之后某個時間點“消費(fèi)者線程”將會執(zhí)行這些函數(shù),就像在監(jiān)聽器被notify函數(shù)激活時執(zhí)行一樣。這項技術(shù)在其他語言中被叫作參數(shù)綁定,剛好適合上面的例子,技術(shù)的實質(zhì)是保存監(jiān)聽器的參數(shù),execute()函數(shù)再直接調(diào)用。如果監(jiān)聽器接收多個參數(shù),處理方法也類似。

需要注意的是如果要保存監(jiān)聽器的執(zhí)行順序,則需要引入綜合排序機(jī)制。方案一中,監(jiān)聽器按照正常的順序激活新線程,這樣可以確保監(jiān)聽器按照注冊的順序執(zhí)行。方案二中,隊列支持排序,其中的函數(shù)會按照進(jìn)入隊列的順序執(zhí)行。簡單來說就是,開發(fā)者需要重視監(jiān)聽器多線程執(zhí)行的復(fù)雜程度,加以小心處理以確保實現(xiàn)所需的功能。

結(jié)束語

觀察者模式在1994年被寫進(jìn)書中以前,就已經(jīng)是主流的軟件設(shè)計模式了,為軟件設(shè)計中經(jīng)常出現(xiàn)的問題提供了很多令人滿意的解決方案。Java一直是使用該模式的引領(lǐng)者,在其標(biāo)準(zhǔn)庫中封裝了這一模式,但是鑒于Java更新到了版本8,十分有必要重新考查經(jīng)典模式在其中的使用。隨著lambda表達(dá)式和其他新結(jié)構(gòu)的出現(xiàn),這一“古老的”模式又有了新的生機(jī)。無論是處理舊程序還是使用這一歷史悠久的方法解決新問題,尤其對經(jīng)驗豐富的Java開發(fā)者來說,觀察者模式都是開發(fā)者的主要工具。

(編譯自:https://dzone.com/articles/the-observer-pattern-using-modern-java)

OneAPM 為您提供端到端的 Java 應(yīng)用性能解決方案,我們支持所有常見的 Java 框架及應(yīng)用服務(wù)器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監(jiān)控從來沒有如此簡單。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方技術(shù)博客。
本文轉(zhuǎn)自 OneAPM 官方博客

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/65517.html

相關(guān)文章

  • Java學(xué)習(xí)路線總結(jié),搬磚工逆襲Java架構(gòu)師(全網(wǎng)最強(qiáng))

    摘要:哪吒社區(qū)技能樹打卡打卡貼函數(shù)式接口簡介領(lǐng)域優(yōu)質(zhì)創(chuàng)作者哪吒公眾號作者架構(gòu)師奮斗者掃描主頁左側(cè)二維碼,加入群聊,一起學(xué)習(xí)一起進(jìn)步歡迎點贊收藏留言前情提要無意間聽到領(lǐng)導(dǎo)們的談話,現(xiàn)在公司的現(xiàn)狀是碼農(nóng)太多,但能獨(dú)立帶隊的人太少,簡而言之,不缺干 ? 哪吒社區(qū)Java技能樹打卡?【打卡貼 day2...

    Scorpion 評論0 收藏0
  • Java 8 API 示例:字符串、數(shù)值、算術(shù)和文件

    摘要:示例字符串?dāng)?shù)值算術(shù)和文件原文譯者飛龍協(xié)議大量的教程和文章都涉及到中最重要的改變,例如表達(dá)式和函數(shù)式數(shù)據(jù)流。不僅僅是字符串,正則表達(dá)式模式串也能受益于數(shù)據(jù)流。 Java 8 API 示例:字符串、數(shù)值、算術(shù)和文件 原文:Java 8 API by Example: Strings, Numbers, Math and Files 譯者:飛龍 協(xié)議:CC BY-NC-SA 4.0 ...

    KavenFan 評論0 收藏0
  • 到處是map、flatMap,啥意思?

    摘要:中使用那一套,線程的速度,你知道的而對于分布式數(shù)據(jù)流來說,本來就是并行的,這種參數(shù)意義就不大了。函數(shù)種類一般作用在數(shù)據(jù)流上的函數(shù),會分為兩類。中的程序是實現(xiàn)在數(shù)據(jù)流上的??梢钥醋魇堑母氯罩荆瑪?shù)據(jù)流中的每一個記錄對應(yīng)數(shù)據(jù)庫中的每一次更新。最近入職一個有趣的年輕同事,提交了大量大量的代碼。翻開git記錄一看,原來是用了非常多的java8的語法特性,重構(gòu)了代碼。用的最多的,就是map、flatM...

    番茄西紅柿 評論0 收藏0
  • 到處是map、flatMap,啥意思?

    摘要:中使用那一套,線程的速度,你知道的而對于分布式數(shù)據(jù)流來說,本來就是并行的,這種參數(shù)意義就不大了。函數(shù)種類一般作用在數(shù)據(jù)流上的函數(shù),會分為兩類。中的程序是實現(xiàn)在數(shù)據(jù)流上的??梢钥醋魇堑母氯罩?,數(shù)據(jù)流中的每一個記錄對應(yīng)數(shù)據(jù)庫中的每一次更新。最近入職一個有趣的年輕同事,提交了大量大量的代碼。翻開git記錄一看,原來是用了非常多的java8的語法特性,重構(gòu)了代碼。用的最多的,就是map、flatM...

    番茄西紅柿 評論0 收藏0
  • 到處是map、flatMap,啥意思?

    摘要:中使用那一套,線程的速度,你知道的而對于分布式數(shù)據(jù)流來說,本來就是并行的,這種參數(shù)意義就不大了。函數(shù)種類一般作用在數(shù)據(jù)流上的函數(shù),會分為兩類。中的程序是實現(xiàn)在數(shù)據(jù)流上的??梢钥醋魇堑母氯罩荆瑪?shù)據(jù)流中的每一個記錄對應(yīng)數(shù)據(jù)庫中的每一次更新。最近入職一個有趣的年輕同事,提交了大量大量的代碼。翻開git記錄一看,原來是用了非常多的java8的語法特性,重構(gòu)了代碼。用的最多的,就是map、flatM...

    xuxueli 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<