1、主要介紹懶漢式,餓漢式,靜態內部類,雙重加鎖,列舉五種單例模式的實作程序
2、針對于單例模式的破解與防護的實作
3、比較其5中方式在多執行緒環境中的效率
package com.behavior_model.danli;
/**
* 單例模式
* @author pshdhx
* 1、保證一個類只有一個實體;
* 2、并且提供一個該實體的全域訪問點
*
* 應用場景:
* 1、windows的任務管理器就是一個典型的單例模式;
* 2、windows的回收站也是個典型的單例應用;在整個運行程序中,回收站一直維護這僅有的一個實體
* 3、網站的計數器,一般也采用單例模式,否則難以同步;
* 4、資料庫的連接池的設計一般也采用單例模式,因為資料庫連接是一種資料資源;、
* 5、【讀取組態檔的類,一般只有一個物件,沒有必要每次使用組態檔的資料,每次new一個物件去讀取】
* 6、Application也是單例的典型應用;Servlet編程中會涉及到
* 7、在Spring中,每個Bean模式是單例的,這樣做的優點是Spring容器可以管理
* 8、在Servlet編程中,每個Servlet也是單例
* 9、在Spring MVC中,控制物件也是單例
*
* 優點:只生成一個實體,減少了系統性能的開銷;當一個物件的產生需要比較多的資源時,【讀取配置,產生其他依賴物件】,則
* 可以通過應用啟動時產生一個單例物件,然后永久駐留記憶體的方式來解決;
* 例如可以設計一個單例類:負責處理資料表的映射
*
* 常見的五種單例模式的實作方式
* 1、餓漢式(執行緒安全,呼叫效率高,但是,不能延時加載)【不能延時】
* 2、懶漢式(執行緒安全,呼叫效率不高,但是可以延時加載)【能延時,我們所期望的】
*
* 3、雙重檢測鎖式(由于JVM底層內部模型的原因,偶爾會出現問題,不建議使用)
* 4、靜態內容類式(執行緒安全,呼叫效率高,但可以延時加載)【能延時】
* 5、列舉單例(執行緒安全,呼叫效率高,不能延時加載)【不能延時】
*
*/
public class DanLi {
public static void main(String[] args) {
EHan e1 = EHan.getInstance();
EHan e2 = EHan.getInstance();
System.out.println(e1==e2); //true
}
}
/**
* 餓漢式-單例模式
*
* static變數會在類裝載時實體化,此時不會涉及多個執行緒訪問該物件的問題
* 虛擬機只保證只會裝載一次該類,肯定不會發生并發訪問的問題,因此,synchronized關鍵字可以省略
*
* 問題:如果只是加載本類,而不是呼叫getInstance(),真是永遠沒有呼叫,則會造成資源浪費,
*
* @author pshdhx
*
*/
class EHan{
private static final EHan eHan = new EHan(); //[加載類的時候,是一個天然的執行緒安全的模式] 沒有延時加載的優勢
private EHan(){}//私有構造器
public static /*synchronized*/ EHan getInstance(){ //不需要同步的話,呼叫效率就高
return eHan;
}
}
//總結:兩私一公
/**
* 懶漢式
* @author pshdhx
* 要點:延遲加載,真正用的的使用才加載
* 問題:資源利用率高了,但是,每次呼叫getInstance()方法都要同步,并發效率低
*/
class LanHanShi{
private static LanHanShi lanHanshi;
private LanHanShi(){};
public static synchronized LanHanShi getInstance(){ //synchronized避免在并發量高的時候創建多個物件
if(lanHanshi==null){
lanHanshi = new LanHanShi();
}
return lanHanshi;
}
}
//總結:兩私一公,不實體化,要同步化
/**
* 雙重檢測鎖模式--可能會出問題
* @author pshdhx
* 同步塊放到了方法內部的if里邊,而不是整個方法的同步,提高了執行的效率;
* 不必每次獲取物件時都進行同步,只有第一次才同步【才進行雙重檢測】,創建了以后就沒有必要了
*
*問題:編譯器優化原因和jvm底層內部模型原因可能會出錯;
*/
class ShuangChongJianCeSuo{
private static ShuangChongJianCeSuo instance = null;
private ShuangChongJianCeSuo(){}
public static ShuangChongJianCeSuo getInstance(){
if(instance==null){
ShuangChongJianCeSuo ins;
synchronized (ShuangChongJianCeSuo.class) {
ins = instance;
if(ins==null){
synchronized (ShuangChongJianCeSuo.class) {
if(ins==null){
ins = new ShuangChongJianCeSuo();
}
}
instance=ins;
}
}
}
return instance;
}
}
//總結:兩私一公,不實體化,第一次進行雙重檢測,三個if;
/**
* 靜態內部類實作
* @author pshdhx
*
*/
class JingTaiNeiBuLei{
private static class JingTaiNeiBuLeiInstance{
private static final JingTaiNeiBuLei instalce = new JingTaiNeiBuLei();
}
private JingTaiNeiBuLei(){}
public static JingTaiNeiBuLei getInstance(){
return JingTaiNeiBuLeiInstance.instalce;
}
}
//總結:兩私一公 :使用了靜態內部類
/**
*
* @author pshdhx
* 列舉型別:使用了enum,底層是jdk的封裝,因此執行緒安全,效率高,但是不能延遲加載
*/
enum MeiJuLeiXing{
INSTANCE;
public void operation(){
System.out.println("我是列舉");
}
}
單例模式除了列舉型別都是可以通過反射和反序列化來破解的,但是也有針對于破解的方法
package com.behavior_model.danli;
import java.io.ObjectStreamException;
import java.io.Serializable;
/**
*
* @author ASUS
*
*/
public class SingletionDemo02 implements Serializable{
//1 創建靜態私有屬性
private static SingletionDemo02 instance;
//2 創建私有構造器 避免外部直接創建物件
// private SingletionDemo02(){}
//防止反射破解單例模式
private SingletionDemo02(){
if (null != instance){
throw new RuntimeException();
}
}
//3 物件提供訪問的公共靜態方法 訪問該屬性
public static synchronized SingletionDemo02 getInstance() throws InterruptedException {
if (null == instance){
instance=new SingletionDemo02();
}
return instance;
}
//方法作用 : 在反序列化的時候 直接呼叫這個方法回傳當前的物件, 而不需要在創建物件
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
package com.behavior_model.danli;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
public class Test {
public static void main (String[] args) {
try {
SingletionDemo02 instance1 = SingletionDemo02.getInstance();
SingletionDemo02 instance2 = SingletionDemo02.getInstance();
System.out.println(instance1);
System.out.println(instance2);
System.out.println("________通過反射方式破解單例模式________");
Class<?> aClass = Class.forName("com.behavior_model.danli.SingletionDemo02");
//獲取默認構造器
Constructor<?> constructor = aClass.getDeclaredConstructor(null);
//跳過安全檢查
// constructor.setAccessible(true);
// SingletionDemo02 singletionDemo02 = (SingletionDemo02) constructor.newInstance();
// SingletionDemo02 singletionDemo03 = (SingletionDemo02) constructor.newInstance();
// System.out.println(singletionDemo02);
// System.out.println(singletionDemo03);
//通過反序列化來破解單例模式
FileOutputStream fos = new FileOutputStream("d:/a.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance1);
oos.close();
fos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
SingletionDemo02 s = (SingletionDemo02) ois.readObject();
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.behavior_model.danli;
import java.util.concurrent.CountDownLatch;
/**
* 比較5中單例模式的效率 [多執行緒環境下]
* @author pshdhx
*
*/
public class Client {
public static void main(String[] args) throws InterruptedException {
long s = System.currentTimeMillis();
int count=10;
final CountDownLatch countDownLatch = new CountDownLatch(count); //相當于10個執行緒計數器,減為0的時候,往下執行
for(int i=0;i<count;i++){
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
for(int j=0;j<10000;j++){
EHan instance = EHan.getInstance();
}
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();// while 回圈等待 main執行緒執行,其他執行緒并行執行,知道其他執行緒執行完成后,main執行緒才往下執行
long e = System.currentTimeMillis();
System.out.println("總耗時:"+(e-s));
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/265351.html
標籤:其他
