第一種寫法: 懶漢式單例
class Singleton{
private static Singleton singleleton=null;
pulic static Singleton getInstance(){
if(singleleton==null){
singleleton=new Singleton()
}
return singleleton;
}
private Singleton(){
singleleton=new Singleton();
}
}
特點:這種寫法是線程不安全的,他無法保證同時不被多個線程訪問,適合在單線程環境下使用。
要想保證線程安全,可以參考下面三種寫法:
1)使用synchronized關鍵字
class Singleton{
private static Singleton singleleton=null;
pulic static synchronized Singleton getInstance(){
if(singleleton==null){
singleleton=new Singleton()
}
return singleleton;
}
private Singleton(){
singleleton=new Singleton();
}
}
特點:相比上個方法,在靜態方法前多加了個synchronized關鍵字,使同一時間只能有一個線程訪問該代碼塊。
2)雙重檢查鎖定
class Singleton{
private static Singleton singleleton=null;
pulic static Singleton getInstance(){
if(singleleton==null){
synchronized(Singleton.class){
if(singleleton==null){
singleleton=new Singleton();
}
}
}
return singleleton;
}
private Singleton(){
singleleton=new Singleton();
}
}
特點:經過兩次判定,第一次檢測到實例爲空時,增加同步,同步後再次檢測到實例爲空時,才創建對象實例。有效防止了在多線程環境下創建多個實例的問題。
3)靜態內部類實現
class Singleton{
private singleton(){}
private static class SingleBuilder{
private static Singleton singleton=new Singleton(); //就像private int int=1
}
public static Singleton getInstance(){
return SingleBuilder.singleton;
}
}
特點:Java中靜態內部類可以訪問其外部類的成員屬性和方法,同時java規定靜態內部類只在首次調用時被加載,因此實現了懶漢式,所以他只被加載一次,所以是線程安全的
2.餓漢式:
class Singleton{
private static Singleton singleleton=new Singleton();
pulic static Singleton getInstance(){
return singleleton;
}
private Singleton(){}
}
特點:餓漢式在類創建的同時就已經創建好一個靜態的對象供系統使用,以後不再改變,所以天生是線程安全的。
3.登記式:
public class Singleton {
private static Map< String,Singleton> map = new HashMap< String,Singleton>();
static{
Singleton single = new Singleton();
map.put(single.getClass().getName(), single);
}
//保護的默認構造子
protected Singleton(){}
//靜態工廠方法,返還此類惟一的實例
public static Singleton getInstance(String name) {
if(name == null) {
name = Singleton.class.getName();
System.out.println("name == null"+"--->name="+name);
}
if(map.get(name) == null) {
try {
map.put(name, (Singleton) Class.forName(name).newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return map.get(name);
}
public static void main(String[] args) {
Singleton3 single = Singleton.getInstance(null);
}
}
登記式單例實際上維護了一組單例類的實例,將這些實例存放在一個Map(登記薄)中,對於已經登記過的實例,則從Map直接返回,對於沒有登記的,則先登記,然後返回。 內部實現還是用的餓漢式單例。
4.使用volatile 關鍵字
class Singleton{
private static volatile Singleton Instance=null;
public Singleton() {
}
public static Singleton getInstance(){
if(Instance==null){
synchronized (Singleton.class) {
if (Instance == null) {
Instance = new Singleton();
}
}
}
return Instance;
}
}
volatile 保證在實例對象創建後快速通知其他線程該對象的實例已經存在,不需要重複創建了。