1. 程式人生 > 實用技巧 >BZOJ-1429 方程的解(雅可比四平方和定理)

BZOJ-1429 方程的解(雅可比四平方和定理)

技術標籤:設計模式設計模式

單例模式

基本介紹

單例模式(Singleton Design Pattern)

一個類只允許建立一個物件(或者例項),那這個類就是一個單例類,這種設計模式就叫做單例設計模式,簡稱單例模式。

為什麼要使用單例模式?

實戰案例一:處理訪問資源衝突

public class Logger{
    //執行緒安全的
    private FilteWriter writer;
    
    public Logger(){
        File file=new File("/xxx");
        writer=new FileWriter
(file,true); } public void log(String message){ writer.write(message); } } //Logger類的應用示例 public class UserController{ private Logger logger=new Logger(); public login(String username,String password,String verifyCode){ logger.log(username+"logined!"
); //***省略邏輯程式碼 } } public class OrderController{ private Logger logger=new Logger(); public void create(OrderVO order){ logger.log("Created an order:"+order.toString()); } }

分析:在UserController和OrderController類中分別建立了一個Logger的物件,寫入的檔案是同一個檔案,所有在寫入的時候會出現執行緒競爭的問題。

public class Logger{
    private FileWriter writer;
    
    public Logger(){
        File file=new File("/xxx");
        writer=new FileWriter(file,true);
    }
    
    public void log(String message){
        //類級別的鎖
        synchronized(Logger.class){
            writer.write(message);
        }
    }
}

實戰案例二:表示全域性唯一類

public class IdGenerator{
    /**
    * AtomicLong 是一個Java併發庫中提供的一個原子變數型別,
    * 它將一些執行緒不安全需要加鎖的複合操作封裝為了執行緒安全的原子操作
    */
    private AtomicLong id=new AtomicLong(0);
    private static final IdGenerator instance=new IdGenerator();
    private IdGenerator(){}
    
    public static IdGenerator getInstance(){
        return instance;
    }
    
    public long getId(){
        return id.incrementAndGet();
    }
}

//使用舉例
long id=IdGenerator.getInstance().getId();

## 如何實現一個單例?

單例的實現方式

關注點:

  • 建構函式需要是private訪問許可權的,這樣才能避免外部通過new的方式建立物件例項
  • 考慮物件建立時的執行緒安全問題
  • 考慮是否支援延遲載入
  • 考慮getInstance()效能是否高效(是否加鎖)
  1. 餓漢式

    public class IdGenerator{
        private AtomicLong id=new AtomicLong(0);
        //建立例項
        private static final IdGenerator instance=new IdGenerator();
        //建構函式設定為私有
        private IdGenerator(){}
        //提供獲取例項物件方法
        public static IdGenerator getInstance(){
            return instance;
        }
        //通過執行緒安全的原子類物件獲取id
        public long getId(){
            return id.incrementAndGet();
        }
    }
    

    分析

    • 餓漢式在類載入的時候就建立好例項,把建立物件的工作放到啟動的時候,啟動時間會受影響,單在程式執行中,會提高速度。

    • 根據fail-fast的設計原則(有問題及時暴露),這個設計也是比較推薦的。

  2. 懶漢式

    public class IdGenerator{
        private AtomicLong id=new AtomicLong(0);
        //建立變數
        private static final IdGenerator instance;
        //建構函式設定為私有
        private IdGenerator(){}
        //提供同步獲取例項物件方法
        public static synchronized IdGenerator getInstance(){
            if(instance==null){
            	instance=new IdGenerator();
            }
            return instance;
        }
        //通過執行緒安全的原子類物件獲取id
        public long getId(){
            return id.incrementAndGet();
        }
    }
    

    分析:

    • 懶漢式個getInstance()這個方法加了一把大鎖,導致這個函式的併發度很低。
    • 如果頻繁的用到,頻繁加鎖、釋放鎖及併發度低等問題,會導致效能瓶頸,這種方式推薦使用
  3. 雙重檢測

    public class IdGenerator{
        private AtomicLong id=new AtomicLong(0);
        //建立變數
        private static volatile final IdGenerator instance;
        //建構函式設定為私有
        private IdGenerator(){}
        //提供同步獲取例項物件方法
        public static IdGenerator getInstance(){
            //第一次判斷
            if(instance==null){ 
                synchronized(IdGenerator.class){
                    //第二次判斷
                    if(instance==null){
            			instance=new IdGenerator();
                    }
                }
            }
            return instance;
        }
        //通過執行緒安全的原子類物件獲取id
        public long getId(){
            return id.incrementAndGet();
        }
    }
    

    分析:雙重檢測模式看似沒有問題,但在低版本的jdk中會出現指令重排問題,為避免出現執行緒問題,還是加上volatile欄位比較好。

  4. 靜態內部類

    public class IdGenerator{
        private AtomicLong id=new AtomicLong(0);
        
        //私有化建構函式
        private IdGenerator(){}
        
        //靜態內部類
        private static class SingletonHolder{
            private static final IdGenerator instance=new IdGenerator();
        }
        //獲取物件例項
        public static IdGenerator getInstance(){
            return SingletonHolder.instance;
        }
        
        public long getId(){
            return id.incrementAndGet();
        }
    }
    

    分析

    • 當外部類IdGenerator載入的時候,並不會建立SingletonHolder例項物件。只有當呼叫getInstance()方法時,SingletonHolder才會被載入,這個時候才會建立instance.
    • instance的唯一性、建立過程的執行緒安全性,都由JVM來保證。
    • 這種實現方式既保證了執行緒安全,又能做到延遲載入。
  5. 列舉

    public enum IdGenerator{
        INSTANCE;
        private AtomicLong id=new AtomicLong(0);
        
        public long getId(){
            return id.incrementAndGet();
        }
    }