一,單例模式
單例模式: 通過一定方法實現整個系統中,對於某個類只能有一個實例存在,並且獲取該類的方法只有唯一的一個;
二,原理類圖
意圖: 保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
適用性:
- 當類只能有一個實例而且客戶可以從一個衆所周知的訪問點訪問它時。
- 當這個唯一實例應該是通過子類化可擴展的,並且客戶應該無需更改代碼就能使用一個擴展的實例時。
三,實例
規則:
- 私有化構造器;
- 在類的內部創建對象;
- 向外暴露一個靜態的公共方法;
1,餓漢式:
/**
* 餓漢式(靜態常量方式)
*/
class EagerSingleton1 {
//私有化構造器
private EagerSingleton1() {
}
//內部直接構造對象
private static final EagerSingleton1 instance = new EagerSingleton1();
//返回對象
public static EagerSingleton1 getInstance() {
return instance;
}
}
優點:方法簡單,類裝載的時候就完成了對象實例化,基於classload機制避免了線程同步問題;
缺點:classload裝載類的方式較多,可能導致對象過早實例化,沒有達到lazy loading效果,沒有使用就會造成內存浪費;
/**
* 餓漢式(靜態代碼塊方式)
*/
class EagerSingleton2 {
private static final EagerSingleton2 instance ;
//靜態代碼塊構造對象
static {
instance = new EagerSingleton2();
}
//私有化構造器
private EagerSingleton2() {
}
//返回對象
public static EagerSingleton2 getInstance() {
return instance;
}
}
優缺點:同靜態常量方式;
2,懶漢式:
/**
* 懶漢式(線程不安全)
*/
class LazySingleton1 {
private LazySingleton1() {
}
private static LazySingleton1 instance;
public static LazySingleton1 getInstance() {
if (instance == null) {
instance = new LazySingleton1();
}
return instance;
}
}
優點:解決了lazy loading問題,需要的時候纔會創建對象;
缺點:多線程情況下線程併發進行再if(instance==null)處可能會重複進入if內部創建多個對象實例,起不到單例作用;
/**
* 懶漢式(線程安全,方法鎖)
*/
class LazySingleton2 {
private LazySingleton2() {
}
private static LazySingleton2 instance;
public static synchronized LazySingleton2 getInstance() {
if (instance == null) {
instance = new LazySingleton2();
}
return instance;
}
}
優點:使用鎖synchronized實現線程解決線程安全問題;
缺點:每次getInstance()都需要加鎖,性能較差;
/**
* 懶漢式(線程不安全,代碼塊鎖)
*/
class LazySingleton3 {
private LazySingleton3() {
}
private static LazySingleton3 instance;
public static LazySingleton3 getInstance() {
if (instance == null) {
synchronized (LazySingleton3.class){
instance = new LazySingleton3();
}
}
return instance;
}
}
看似可以解決線程安全和效率問題,但是代碼塊鎖依然存在安全問題,當if進入之後synchronized依然可以創建多個實例;
3,雙重檢索:
/**
* 雙重檢索(安全高效)
*/
class DoubleCheckSingleton{
private DoubleCheckSingleton() {
}
private static volatile DoubleCheckSingleton instance;
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class){
if(instance == null){
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
使用volatile做內存共享,兩次使用if (instance == null)檢查加上synchronized 實現線程安全;
4,靜態內部類:
/**
* 靜態內部類
*/
class InnerStaticSingleton{
private InnerStaticSingleton() {
}
private static class Inner{
private static final InnerStaticSingleton instance=new InnerStaticSingleton();
}
public static InnerStaticSingleton getInstance() {
return Inner.instance;
}
}
1、通過類裝載機制保證初始化實例時只有一個線程;
2、內部類在外部類裝載的時候不會初始化,只有在調用getInstance()時纔會初始化,保證了lazy loading;
3、類的靜態屬性只會在第一次裝載的時候初始化一次,保證了單例;
5,枚舉:
/**
* 枚舉就是單例
*/
enum EnumSingleton{
INSTANCE;
}
使用jdk自帶枚舉,可以避免多線程問題,也可以防止反序列化創建新對象;
四,源碼分析
jdk源碼中較多使用單例模式,如Runtime;
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
...
...
...
}