(一)設計模式之單例模式(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;
}
}
}
}