零、什么是單例模式?
單例,單例,顧名思義這個類從頭至尾只有一個實(shí)例。
單例模式具備以下三個特點(diǎn):
私有化的構(gòu)造函數(shù)
私有的靜態(tài)的全局變量
公有的靜態(tài)的方法
常用的三種單例模式:
懶漢模式(線程不安全)
餓漢模式(線程安全,資源利用率不高)
雙重校驗(yàn)鎖模式(線程安全,資源利用率高)
一、懶漢模式(線程不安全)
類加載的時候,“懶”的去創(chuàng)建一個實(shí)例;
當(dāng)?shù)谝淮伪皇褂玫臅r候我才給你創(chuàng)建實(shí)例,延遲加載
示例代碼如下:
/**
* 懶漢式
* 線程不安全,延遲初始化
* @author :Negen
* @Date :Created in 15:25 2020/5/23
* @Description:
* @Modified By:
* @Version: 1.0
*/
public class SingletonLazy {
//私有的靜態(tài)的全局變量
private static SingletonLazy instanse;
//私有化的構(gòu)造函數(shù)
private SingletonLazy(){};
//公有的靜態(tài)的方法
public static SingletonLazy getInstance(){
if (null == instanse){ //線程不安全觸發(fā)點(diǎn)
instanse = new SingletonLazy();
}
return instanse;
}
}
二、餓漢模式(線程安全)
類加載的時候就創(chuàng)建一個實(shí)例;
創(chuàng)建的實(shí)例如果一直沒有使用的話,就會造成資源浪費(fèi);
實(shí)例代碼如下:
/**
* 餓漢模式
* 線程安全,常用
* 一開始就創(chuàng)建了實(shí)例,如果一直沒用,就是產(chǎn)生的垃圾
* @author :Negen
* @Date :Created in 15:30 2020/5/23
* @Description:
* @Modified By:
* @Version: 1.0
*/
public class SingletonHungary {
private static SingletonHungary instance = new SingletonHungary();
private SingletonHungary(){};
public static SingletonHungary getInstance(){
return instance;
}
}
三、雙重校驗(yàn)鎖模式(線程安全)
對實(shí)例進(jìn)程兩次是否為空的檢查;
第一次檢查是為了防止不必要的鎖;
第二次就是例行檢查;
示例代碼如下:
/**
* 雙重檢查模式
*
* 第一次:避免不必要的上鎖
* 第二次:例行檢查
* @author :Negen
* @Date :Created in 16:28 2020/5/23
* @Description:
* @Modified By:
* @Version: 1.0
*/
public class SingletonDoubleCheckLock {
private volatile static SingletonDoubleCheckLock instance;
private SingletonDoubleCheckLock(){};
public static SingletonDoubleCheckLock getInstance(){
if (null == instance){
synchronized (SingletonDoubleCheckLock.class){
if (null == instance){
instance = new SingletonDoubleCheckLock();
}
}
}
return instance;
}
}
四、線程不安全體現(xiàn)在哪兒?
如果有兩個線程同時進(jìn)入 if(instance == null)階段,那么它們都會進(jìn)入創(chuàng)建實(shí)例的階段,從而導(dǎo)致創(chuàng)建多個不同的實(shí)例。
下面用多線程模擬了三種模式創(chuàng)建實(shí)例的結(jié)果
1、懶漢模式
示例代碼:
public class TestLazy implements Runnable{
public static void main(String[] args) {
for (int i = 0; i<10; i++){
TestLazy test = new TestLazy();
new Thread(test).start();
}
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "----" + SingletonLazy.getInstance().toString());
}
}
打印結(jié)果:

十個線程中出現(xiàn)了三個不同的地址,說明“懶漢模式”線程不安全
2、餓漢模式
示例代碼:
public class TestHungary implements Runnable{
public static void main(String[] args) {
for (int i = 0; i < 10; i++){
TestHungary test = new TestHungary();
new Thread(test).start();
}
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "----" + SingletonHungary.getInstance().toString());
}
}
打印結(jié)果:
[圖片上傳失敗...(image-34b196-1590225907614)]
十個線程中出現(xiàn)的都是相同的地址,說明“餓漢模式”線程安全
3、雙重校驗(yàn)鎖模式
示例代碼:
public class TestDoubleCheckLock implements Runnable{
public static void main(String[] args) {
for (int i = 0; i < 10; i++){
TestDoubleCheckLock testDoubleCheckLock = new TestDoubleCheckLock();
new Thread(testDoubleCheckLock).start();
}
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + "----" + SingletonDoubleCheckLock.getInstance().toString());
}
}
打印結(jié)果:
[圖片上傳失敗...(image-96367b-1590225907614)]
十個線程中出現(xiàn)的都是相同的地址,說明“雙重校驗(yàn)鎖模式”線程安全