1.1 單例模式
單例模式是設計模式中非常經典的一種。那么有同學肯定就會好奇了,什么是設計模式呢?
設計模式簡單的說就是程序員的“棋譜”,我們下象棋時肯定或多或少都背過棋譜,例如當頭炮、馬后炮等,設計模式也是這樣,開發過程中常常會遇到一些經典場景,用固定的解決方案去解決這些問題,這就是設計模式,今天我們先來介紹一下單例模式。
單例模式是指某個類只能實例化一個對象,不能有多個,這一點在很多場景中都會用到,例如數據庫中的DateSource實例就只需要一個;單例模式又有兩種實現方式,分別為餓漢模式和懶漢模式。
1.1.1 餓漢模式
在類加載的時候,創建實例(比較急切)
可以看到,由于將構造方法設為私有,所以當我們想要自己實例化一個對象的時候,編譯器會報錯,此時,這個類將只會有加載類時實例化的那一個對象,無法再實例化出其它對象。
1.1.2 懶漢模式
只有當需要的時候才創建實例
上述代碼可以看出,只有當首次調用getInstance方法時才會創建實例,否則將不會創建實例。
單線程中懶漢模式沒有問題,但是放在多線程中,就會存在許多bug!
1.1.3 懶漢模式(多線程版)
首先要解決一個疑惑,為什么餓漢模式的單線程都不會有bug,但是懶漢模式的多線程會存在bug呢?是因為餓漢模式的getInstance方法只有讀功能,但是懶漢模式的getInstance方法卻既可讀又可寫,兩個線程對于同一個變量進行寫操作,就很容易出現問題。所以我們要加鎖,將操作變為原子性的,這樣就不會出現問題了。
1、這里的加鎖是為了保證原子性操作,防止代碼出bug(new多個對象出來)
2、這里再一次判斷對象是否為空是為了確定要不要加鎖,因為如果不加這個代碼的話,每次調用這個方法都會加鎖,但是加鎖很費時,效率很低,不好!
3、此處的volatile關鍵字是為了防止出現指令重排序問題。
1.2 阻塞隊列
阻塞隊列是多線程代碼中常用的一種數據結構。
所謂阻塞隊列,其實就是加了阻塞功能的隊列。
a)如果隊列為空,繼續出隊列,就會發生阻塞,直到其它線程向隊列中加入元素。
b)如果隊列為滿,繼續入隊列,也會發生阻塞,直到其它線程向隊列中取走元素。
阻塞隊列,最大的意義是用來實現“生產者消費者模型”
“生產者消費者”模型的意義:
1、解耦合(單獨設置一個隊列,使原本兩個直接進行數據交互的元素通過這個隊列進行交互)
2、削峰填谷
當數據請求量很大的時候,如果沒有阻塞隊列的話,服務器A一直給服務器B發數據,就可能使服務器B崩潰;但是如果設置一個阻塞隊列,那么A只需要將數據放入隊列中,如果B此時已達到最大處理負荷的話,那B只需要慢慢的取出隊列中的數據處理即可;反之,如果此時數據請求量很小,那么服務器B就可以慢慢地消耗掉堆積在阻塞隊列中的數據。
1.3 阻塞隊列的代碼實現
以上代碼就是自己實現了一個簡易的阻塞隊列,可以用作一個簡單的“生產者消費者”模型。注意兩個wait和notify都是互相喚醒的,無法自己喚醒自己。