java單列模式(從0開始分析)
Java中的單例模式的資料已經有很多了,很優秀的也有許多,但是我的部落格,還是為小白寫的。(因為我也是小白啊!)希望你可以按我的步驟一起學習一下,我已經寫的很詳細了,寫一遍大於看一遍!!!如果你是大神也不要緊,你可以直接看這篇部落格(我大部分想法都來源於他),或者幫我看看我有沒有錯,也可以看看有沒有用當年沒有搞清楚的。----(寫所有給看的人)最重要的是希望你能幫我看看哪裡不對,或者有問題、不合理都可以告訴我,我們一起學習一下。(謝謝啦)
聯絡方式:QQ:1441289824
————————————————————————華麗的分割線—————————————————————————
java中有眾多設計模式,其中最常用的就是單例模式。大家肯定看到許多單例模式和用過,接下我先說一下單例模式的區別和用法,然後從簡單的開始學習。
單例模式:單例物件能保證在一個JVM中,該物件只有一個例項存在。
常見的五種單例模式實現方式 主要:
-餓漢式(執行緒安全,呼叫效率高;但是不能延時載入)
-懶漢式(執行緒安全,呼叫效率不高,但是可以延時載入)
其他: -雙重檢測鎖式(由於JVM底層內部模型原因,偶爾會出問題,不建議使用) -靜態內部類式(執行緒安全,呼叫效率高,但是,可以延時載入)
-列舉類(執行緒安全,呼叫效率高,防反射和反序列化,不能延時載入) 如何選用?
-單例物件 佔用資源少,不需要延時載入時: 列舉類 好於 餓漢式 -單例物件 佔用資源大,需要延時載入時:
靜態內部類式 好於 懶漢式
1.接下來從最簡單的單例模式開始吧!直接上程式碼:
public class Android { /*宣告一個變數等於null,實現了延遲載入*/ private static Android android = null; /*構造方法私有化,防止被new出來*/ private Android() { } /*呼叫這個方法時判斷物件不存在時建立物件*/ public static Android getinstance() { if (android == null) { android = new Android(); } return android; } }
大部分看過單例文章,都知道這個單例執行緒不安全唄。為什麼不安全呢,當時小白的我就沒有搞懂,尤其是我這種自學的單例,只是抄別人的,什麼都不懂。(哈哈)
哪裡不安全呢,我給大家一段測試程式碼,你就可以看出來了。
public class ExampleUnitTest {
@Test
public void test() {
MyThread myThread =new MyThread();
MyThread myThread1 =new MyThread();
myThread.start();
myThread1.start();
}
class MyThread extends Thread {
@Override
public void run() {
Android getinstance = Android.getinstance();
System.out.println("多執行緒下: " + getinstance.toString());
}
}
}
我的第一次接下是測試結果:
多執行緒下: [email protected] 多執行緒下: [email protected]
有同學說了他的結果和我一樣是這樣的:
多執行緒下: [email protected] 多執行緒下: [email protected]
不要緊,我來解釋一下原因。第一次的結果是這樣來的。因為程式碼的執行速度很快,即便是兩個執行緒。一個執行緒走到if判斷時,另一個也到了if判斷,他們兩個都得到物件是null的結果,就各自例項化了物件。(這也是執行緒不安全的原因)
第二次的結果原因大家應該理解了,就是說。一個執行緒快,一個執行緒慢,快的例項化後物件。第二個執行緒才判斷,此時的物件已經被例項化過了,所以if判斷是不等於null。
執行緒的速度為什麼會快慢呢,這時的你需要理解多執行緒去了。
2.加鎖的單例模式
在多執行緒模式下,大家能想到第一個就是synchronized,這個關鍵字吧。這個關鍵字我就不在這說了。直接上程式碼吧!
這個單例是為了彌補上面單例在多執行緒下例項了兩個物件的不足。
public class Android {
private static Android android = null;
private Android() {
}
public synchronized static Android getinstance() {
if (android == null) {
android = new Android();
}
return android;
}
}
對,基本沒有變化。只是加了一個synchronized的關鍵字而已。看測試效果:
第一次:
多執行緒下: [email protected] 多執行緒下: [email protected]
第二次: 多執行緒下: com.example.a14412.myapplication.Andro[email protected]
第三次
已經結束了,對你猜對了,第三次沒有列印任何東西。你說你是不這樣的,好吧。你先看看和我的程式碼一樣嗎?
如果一樣的話,請多執行幾次,看看。這三種結果,都遇到了嗎?
遇到了,我們繼續。
原因很簡單啊,主執行緒先結束了,子執行緒執行不到列印的程式碼了。修改加個sleep試試。
public class ExampleUnitTest {
@Test
public void test() {
MyThread myThread =new MyThread();
MyThread myThread1 =new MyThread();
myThread.start();
myThread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class MyThread extends Thread {
@Override
public void run() {
Android getinstance = Android.getinstance();
System.out.println("多執行緒下: " + getinstance.toString());
}
}
}
列印結果:
第一次:
多執行緒下: [email protected] 多執行緒下: [email protected]
第二次:
多執行緒下: [email protected] 多執行緒下: [email protected]
第三次:
多執行緒下: [email protected] 多執行緒下: [email protected]
這個單例,沒有問題了?但是我們可以優化的。為什麼要優化呢。從效能上看。當一個執行緒進入能得到同步鎖,加上鎖以後,第二個執行緒只能等待。如果這時那個執行緒已經例項化物件,由於有同步鎖,另一個執行緒還要等待。小程式就沒事了,如果程式大了。每次例項化這個物件,都要有些執行緒在等待。,就會很耗時和耗效能。上程式碼
3.雙重檢測鎖式
public class Android {
private static Android android = null;
private Android() {
}
public static Android getinstance() {
if (android == null) {
synchronized (Android.class) {
if (android==null) {
android = new Android();
}
}
}
return android;
}
}
雙重if判斷,這是已經搞定了。但是這個單例還是不推薦。雙重if,容易出問題。在程式大了,程式碼複雜了,就不是這麼簡單的使用了。說不定會出現什麼未知情況,比如死鎖啊,等待。以上都是懶漢式是單例。(延時載入)
那我也就不羅嗦了,寫我最終版的單例模式吧!
4.內部類單例
先上程式碼:
public class Android {
private Android() {
}
public static Android getAndroidHolder(){
return AndroidHolder.android;
}
private static class AndroidHolder{
private final static Android android=new Android();
}
}
內部類的寫法。在類裡寫個內部類。由於內部類是private 的只有Androi可以訪問的。那怎麼訪問,這不是還有一個public static 的方法嗎?由於內部類是 static 的,就可以使用 AndroidHolder.android獲取的就是new Android這個物件了。
看測試程式碼:
public class ExampleUnitTest {
@Test
public void test() {
MyThread myThread =new MyThread();
MyThread myThread1 =new MyThread();
myThread.start();
myThread1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class MyThread extends Thread {
@Override
public void run() {
Android androidHolder = Android.getAndroidHolder();
//列印的是地址值
System.out.println("多執行緒下1: " + androidHolder.toString());
//列印的是類名
System.out.println("多執行緒下1: " + androidHolder.getClass().getName());
}
}
再看結果:
多執行緒下1: [email protected] 多執行緒下1: com.example.a14412.myapplication.Android 多執行緒下1: [email protected] 多執行緒下1: com.example.a14412.myapplication.Android
單例模式到這就結束了,Android開發的有福利了。送你給外掛,SingletonTest.
SingletonTest用法很簡單,百度一下就好。