1. 程式人生 > >(一)設計模式之單例模式(singtonMode)

(一)設計模式之單例模式(singtonMode)

參考文章:

(1)https://blog.csdn.net/li295214001/article/details/48135939

(2)http://love-love-l.blog.163.com/blog/static/21078304201001804211901/

(3)https://blog.csdn.net/eff666/article/details/67640648

(4)https://blog.csdn.net/jason0539/article/details/23297037

參考圖書:

《軟體設計模式與體系結構》

概念:

單例模式是值確保一個類僅有一個唯一的例項,並且提供一個全域性的訪問點!

(一)懶漢式單例:(又分為執行緒安全的和執行緒不安全的)

          (1.1) 懶漢式執行緒不安全的:

public class Sington {

	// 私有化構造器
	private Sington() {
	}

	// 提供一個靜態的該類物件
	private static Sington instance = null;

	// 生成該屬性的public的方法,以供外界通過這個方法獲得該類的例項
	public static Sington getInstance() {
		if(instance == null) {
			//如果當前屬性值為null,返回一個新構建的
			instance= new Sington();
		}
		//如果不是那麼就不再構建,直接返回已經存在的
		return instance;
	}


	public static void main(String[] args) {
		// 建立兩個物件
		Sington s1 = Sington.getInstance();
		Sington s2 = Sington.getInstance();

		// 判斷是否為同一個Sington物件,如果結果為true,說明則為單例
		System.out.println(s1 == s2);

	}

}

 結果為true!!

這種方式,大家也看到了,並沒有對多執行緒進行考慮,一旦有多個執行緒同時請求,那就並不能保證單一性!!

使用上面的方式測試多執行緒:

public class Sington {

	// 私有化構造器
	private Sington() {
	}

	// 提供一個靜態的該類物件
	private static Sington instance = null;

	// 生成該屬性的public的方法,以供外界通過這個方法獲得該類的例項
	public static Sington getInstance() {
		if(instance == null) {
			//如果當前屬性值為null,返回一個新構建的
			instance= new Sington();
		}
		//如果不是那麼就不再構建,直接返回已經存在的
		return instance;
	}


	public static void main(String[] args) {
		// 建立兩個物件
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s1 = Sington.getInstance();
				System.out.println(s1);
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s2 = Sington.getInstance();
				System.out.println(s2);
			}
		},"t2");

		
		t1.start();
		t2.start();
	}

}

兩個輸出的物件:不一致

s1 [email protected]
s2 [email protected]

           (1.2)懶漢式執行緒安全的(分為三種)

                     (1.2.1)java為上面出現的情況提供了一個關鍵字synchronized來處理上面的情況

接下來,是懶漢式單例的(考慮執行緒安全的方式)

public class Sington {

	// 私有化構造器
	private Sington() {
	}

	// 提供一個靜態的該類物件
	private static Sington instance = null;

	//對該方法加入synchronized關鍵字,為防止多執行緒的物件建立
	public static synchronized Sington getInstance() {
		if(instance == null) {
			//如果當前屬性值為null,返回一個新構建的
			instance= new Sington();
		}
		//如果不是那麼就不再構建,直接返回已經存在的
		return instance;
	}


	public static void main(String[] args) {
		/**
		 * 此時建立的兩個物件是利用多執行緒建立的,但是加上synchronized關鍵字之後,
		 * 第一個執行緒在建立的時候,第二個執行緒是無法進入物件的該方法的!!
		 */
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s1 = Sington.getInstance();
				System.out.println(s1);
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s2 = Sington.getInstance();
				System.out.println(s2);
			}
		},"t2");

		
		t1.start();
		t2.start();
	}

}

結果:

s1 [email protected]
s2 [email protected]

 

(1.2.2)懶漢式的處理多執行緒的第二種方式:(雙重檢查鎖)

對於懶漢式的處理多執行緒的第一種方式,相信大家多看到了,是可以解決多執行緒的問題,但是,比較的耗費資源,因為每一個執行緒通過物件呼叫該物件,都需要排隊等待,因為前面有一個執行緒執行了同步鎖!接下來,對上面的方式進行部分的優化,程式碼我就不貼全部了,只貼改了的部分,其它都一樣!

//對該方法加入synchronized關鍵字,為防止多執行緒的物件建立
	public static Sington getInstance() {
		
		//如果第二個執行緒也進來,但是當前物件不為空,那麼就可以直接拿走
		//不需要在等到進入後,得知,不為空再拿走!!
		if(instance == null) {
			//只是鎖住程式碼塊
			synchronized(Sington.class) {
				//如果當前屬性值為null,返回一個新構建的
				instance= new Sington();
			}
		}
		//如果不是那麼就不再構建,直接返回已經存在的
		return instance;
	}

(1.2.3)懶漢式的處理多執行緒的第三種方式:靜態內部類

這種方法肯定安全,因為至始至終就一個物件!!

public class Sington {

	// 私有化構造器
	private Sington() {
	}
	
	// 提供一個靜態的內部類
	private static class staticInsideClass{
		private static final Sington instance = new Sington();
	}


	//通過靜態內部類的fina返回物件例項
	public static Sington getInstance() {
		return staticInsideClass.instance;
	}


	public static void main(String[] args) {
		/**
		 * 此時建立的兩個物件是利用多執行緒建立的,但是加上synchronized關鍵字之後,
		 * 第一個執行緒在建立的時候,第二個執行緒是無法進入物件的該方法的!!
		 */
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s1 = Sington.getInstance();
				System.out.println(s1);
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s2 = Sington.getInstance();
				System.out.println(s2);
			}
		},"t2");

		
		t1.start();
		t2.start();
	}

}

結果:一致!

s1 [email protected]
s2 [email protected]

 

 


 

(二)第二種餓漢式單例模式(就是不管你有沒有用,我一上來就已經把物件建立好,就那一個物件,用你就拿去!)

public class Sington {

	// 私有化構造器
	private Sington() {
	}
	//一開始就在其類的內部建立好,等用了,就給
	//至始至終就這一個物件
	private final static Sington instace = new Sington();
	
	public static Sington getInstance() {
		return Sington.instace;
	}
	

	public static void main(String[] args) {
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s1 = Sington.getInstance();
				System.out.println(s1);
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				Sington s2 = Sington.getInstance();
				System.out.println(s2);
			}
		},"t2");

		
		t1.start();
		t2.start();
	}

}

結果:一致!

s1 [email protected]
s2 [email protected]

實際使用舉例:

Client:

package cn.gxm.mode.test;

import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;

public class Client extends JFrame implements ActionListener{

	private TextArea area = new TextArea("click to get connection");
	private Button connectionButton = new Button("create connection");
	private Button exitButton = new Button("exit"); 
	public Client() {
		super("客戶端");
		setBounds(200,300, 400, 400);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(new FlowLayout());
		
		connectionButton.addActionListener(this);
		exitButton.addActionListener(this);
		
		add(area);
		add(connectionButton);
		add(exitButton);
		
		setVisible(true);
		validate();
		
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == connectionButton) {
			LoginService login = LoginService.getInstance();
			area.setText(login.getMesssage());
		}else if(e.getSource() == exitButton) {
			dispose();
		}
	}
	
	public static void main(String[] args) {
		Client c= new Client();
	}
	
}

LoginService:

package cn.gxm.mode.test;

import java.awt.Button;
import java.awt.FlowLayout;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;

public class LoginService extends JFrame {
	
	private TextField username = new TextField("張三");
	private TextField password = new TextField("123456");
	private Button loginButton = new Button("Login");
	private Button exitButton = new Button("exit"); 
	
	//測試是否已經在連線了(預設為false)
	private static boolean isLogin = false;
	
	//告訴客戶端請求連結的資訊
	public String messsage;
	
	private static LoginService instance = null;
	
	public String getMesssage() {
		return messsage;
	}

	public void setMesssage(String messsage) {
		this.messsage = messsage;
	}



	public static LoginService getInstance() {
		if(instance == null) {
			synchronized (LoginService.class) {
				instance = new LoginService();
			}
		}
		return  instance;
	}
	
	
	
	private LoginService() {
		super("請求網路登陸端");
		setBounds(200,300, 400, 400);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setLayout(new FlowLayout());
		
		loginButton.addActionListener(new MyActionLister());
		exitButton.addActionListener(new MyActionLister());
		
		add(loginButton);
		add(exitButton);
		add(username);
		add(password);
		
		setVisible(true);
		validate();
		
	}
	
	
	class MyActionLister implements ActionListener{
		@Override
		public void actionPerformed(ActionEvent e) {
			if(e.getSource() == loginButton) {
				if(isLogin) {
					//說明已經有connection了,則告訴客戶端錯誤資訊
					messsage = "連線已經開啟了,請不要重複開啟!!";
				}else {
					isLogin = true;
					messsage = "連線開啟成功!!";
				}
			}else if(e.getSource() == exitButton) {
				dispose();
				//重置isLogin
				isLogin = false;
			}
		}
	}
	
}