文章目錄
- 簡介
- 單例模式的延遲加載
- double check模式
- 靜態域的實作
- ThreadLocal版本
簡介
雙重檢測鎖定模式是一種設計模式,我們通過首次檢測鎖定條件而不是實際獲得鎖從而減少獲取鎖的開銷,
雙重檢查鎖定模式用法通常用于實作執行延遲初始化的單例工廠模式,延遲初始化推遲了成員欄位或成員欄位參考的物件的構造,直到實際需要才真正的創建,
但是我們需要非常小心的使用雙重檢測模式,以避免發送錯誤,
單例模式的延遲加載
先看一個在單執行緒正常作業的單例模式:
public class Book {
private static Book book;
public static Book getBook(){
if(book==null){
book = new Book();
}
return book;
}
}
上面的類中定義了一個getBook方法來回傳一個新的book物件,回傳物件之前,我們先判斷了book是否為空,如果不為空的話就new一個book物件,
初看起來,好像沒什么問題,我們仔細考慮一下:
book=new Book()其實一個復雜的命令,并不是原子性操作,它大概可以分解為1.分配記憶體,2.實體化物件,3.將物件和記憶體地址建立關聯,
在多執行緒環境中,因為重排序的影響,我們可能的到意向不到的結果,
最簡單的辦法就是加上synchronized關鍵字:
public class Book {
private static Book book;
public synchronized static Book getBook(){
if(book==null){
book = new Book();
}
return book;
}
}
double check模式
如果要使用double check模式該怎么做呢?
public class BookDLC {
private static BookDLC bookDLC;
public static BookDLC getBookDLC(){
if(bookDLC == null ){
synchronized (BookDLC.class){
if(bookDLC ==null){
bookDLC=new BookDLC();
}
}
}
return bookDLC;
}
}
我們先判斷bookDLC是否為空,如果為空,說明需要實體化一個新的物件,這時候我們鎖住BookDLC.class,然后再進行一次為空判斷,如果這次不為空,則進行初始化,
那么上的代碼有沒有問題呢?
有,bookDLC雖然是一個static變數,但是因為CPU快取的原因,我們并不能夠保證當前執行緒被賦值之后的bookDLC,立馬對其他執行緒可見,
所以我們需要將bookDLC定義為volatile,如下所示:
public class BookDLC {
private volatile static BookDLC bookDLC;
public static BookDLC getBookDLC(){
if(bookDLC == null ){
synchronized (BookDLC.class){
if(bookDLC ==null){
bookDLC=new BookDLC();
}
}
}
return bookDLC;
}
}
靜態域的實作
public class BookStatic {
private static BookStatic bookStatic= new BookStatic();
public static BookStatic getBookStatic(){
return bookStatic;
}
}
JVM在類被加載之后和被執行緒使用之前,會進行靜態初始化,而在這個初始化階段將會獲得一個鎖,從而保證在靜態初始化階段記憶體寫入操作將對所有的執行緒可見,
上面的例子定義了static變數,在靜態初始化階段將會被實體化,這種方式叫做提前初始化,
下面我們再看一個延遲初始化占位類的模式:
public class BookStaticLazy {
private static class BookStaticHolder{
private static BookStaticLazy bookStatic= new BookStaticLazy();
}
public static BookStaticLazy getBookStatic(){
return BookStaticHolder.bookStatic;
}
}
上面的類中,只有在呼叫getBookStatic方法的時候才會去初始化類,
ThreadLocal版本
我們知道ThreadLocal就是Thread的本地變數,它實際上是對Thread中的成員變數ThreadLocal.ThreadLocalMap的封裝,
所有的ThreadLocal中存放的資料實際上都存盤在當前執行緒的成員變數ThreadLocal.ThreadLocalMap中,
如果使用ThreadLocal,我們可以先判斷當前執行緒的ThreadLocal中有沒有,沒有的話再去創建,
如下所示:
public class BookThreadLocal {
private static final ThreadLocal<BookThreadLocal> perThreadInstance =
new ThreadLocal<>();
private static BookThreadLocal bookThreadLocal;
public static BookThreadLocal getBook(){
if (perThreadInstance.get() == null) {
createBook();
}
return bookThreadLocal;
}
private static synchronized void createBook(){
if (bookThreadLocal == null) {
bookThreadLocal = new BookThreadLocal();
}
perThreadInstance.set(bookThreadLocal);
}
}
本文的代碼:
learn-java-base-9-to-20/tree/master/security
本文已收錄于 http://www.flydean.com/java-security-code-line-double-check-lock/
最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/174883.html
標籤:其他
