http://c.biancheng.net/view/1338.html
https://www.cnblogs.com/zhucj-java/p/13534551.html
https://www.cnblogs.com/restartyang/articles/7770856.html
https://www.imooc.com/article/310708
單例模式定義
單例模式是指一個(gè)類只有一個(gè)實(shí)例,而且該類可以自己創(chuàng)建自己的實(shí)例。
單例模式的應(yīng)用
1.在高并發(fā)的系統(tǒng)中生成單號要求唯一,可以將生成單號的實(shí)例寫為單例模式
2.數(shù)據(jù)庫中的連接池
3.枚舉類
4.系統(tǒng)中的上下文
5.spring中@Autowired默認(rèn)注入的是單例注入
6.spring中在需要@Autowired默認(rèn)注入的是多例注入時(shí)可在需要注入的類加上@Scope(value="prototype")---prototype原型模式,每次獲取Bean的時(shí)候會有一個(gè)新的實(shí)例
單例模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1.由于在系統(tǒng)內(nèi)存中只存在一個(gè)對象,因此可以 節(jié)約系統(tǒng)資源,當(dāng) 需要頻繁創(chuàng)建和銷毀的對象時(shí)單例模式無疑可以提高系統(tǒng)的性能。
2.單例模式設(shè)置全局訪問點(diǎn),可以優(yōu)化和共享資源的訪問。
3.避免對共享資源的多重占用。
缺點(diǎn)
1.單例模式?jīng)]有接口,擴(kuò)展困難,則除了修改原來的代碼,沒有第二種途徑,違背開閉原則。
單例模式的實(shí)現(xiàn)
分為餓漢式和懶漢式、其中餓漢式在類被加載時(shí)就創(chuàng)建了實(shí)例、懶漢式在第一次調(diào)用時(shí)創(chuàng)建實(shí)例(在多線程下有線程安全問題)
餓漢式實(shí)現(xiàn)
/**
* 單例模式-餓漢式
* 餓漢式不管有沒有調(diào)用getInstance()方法,都會預(yù)先在系統(tǒng)中創(chuàng)建一個(gè)靜態(tài)對象
* 餓漢式優(yōu)點(diǎn): 在多線程模式下是安全的
* 缺點(diǎn): 沒有調(diào)用方法前就被加載,會占用內(nèi)存
*/
public class BaJie extends JPanel {
private static volatile BaJie instance = new BaJie();
private BaJie(){
JLabel j = new JLabel(new ImageIcon("src/main/resources/img/Bajie.jpg"));
this.add(j);
}
public static BaJie getInstance(){
return instance;
}
public static void main(String[] args) {
JFrame jf = new JFrame("餓漢式單例模式測試");
jf.setLayout(new GridLayout(1, 2));
Container contentPane = jf.getContentPane();
BaJie baJie = BaJie.getInstance();
contentPane.add(baJie);
BaJie baJie2 = BaJie.getInstance();
contentPane.add(baJie2);
if(baJie == baJie2){
System.out.println("他們是同一個(gè)人");
} else {
System.out.println("他們不是同一個(gè)人");
}
jf.pack();
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
運(yùn)行結(jié)果:
他們是同一個(gè)人
懶漢式實(shí)現(xiàn)
為解決在多線程下不安全對創(chuàng)建實(shí)例的方法加鎖synchronized
/**
* 單例模式--懶漢式
* 該模式的特點(diǎn)是類加載時(shí)沒有生成單例,只有當(dāng)?shù)谝淮握{(diào)用 getlnstance 方法時(shí)才去創(chuàng)建這個(gè)單例
* 懶漢式優(yōu)點(diǎn):只有調(diào)用方法才創(chuàng)建對象,不會占用內(nèi)存
* 缺點(diǎn):在多線程模式下不安全
*/
public class President {
private static volatile President instance = null;
//private 避免類在外部被實(shí)例化
private President(){
System.out.println("生產(chǎn)一個(gè)美國總統(tǒng)");
}
/**
* synchronized為解決多線程下懶漢式存在的線程安全問題
* synchronized 給對象和方法或代碼塊加鎖,使得同一時(shí)刻只有一個(gè)線程執(zhí)行該代碼
* @return
*/
public static synchronized President getInstance(){
if (instance == null){
instance = new President();
} else {
System.out.println("已有美國總統(tǒng),不能再生產(chǎn)!");
}
return instance;
}
public void getName(){
System.out.println("獲得一個(gè)美國總統(tǒng):特朗普");
}
public static void main(String[] args) {
//線程1
Thread thread1 = new Thread(){
@Override
public void run(){
President d1 = President.getInstance();
System.out.println(d1.hashCode());
}
};
thread1.start();
//線程2
Thread thread2 = new Thread(){
@Override
public void run(){
President d2 = President.getInstance();
System.out.println(d2.hashCode());
}
};
thread2.start();
President d3 = President.getInstance();
System.out.println(d3.hashCode());
}
}
main執(zhí)行結(jié)果:
生產(chǎn)一個(gè)美國總統(tǒng)
338093198
已有美國總統(tǒng),不能再生產(chǎn)!
338093198
已有美國總統(tǒng),不能再生產(chǎn)!
338093198
在多線程下通過加鎖可以解決線程安全問題
如果不加synchronized執(zhí)行結(jié)果為:
生產(chǎn)一個(gè)美國總統(tǒng)
生產(chǎn)一個(gè)美國總統(tǒng)
1587888397
已有美國總統(tǒng),不能再生產(chǎn)!
1368198263
1587888397
產(chǎn)生了1587888397、1368198263兩個(gè)實(shí)例
擴(kuò)展為有限的多例模式Multitcm
生成指定個(gè)數(shù)的實(shí)例存到Map中,使用時(shí)隨機(jī)取一個(gè)使用
有限的多例模式實(shí)現(xiàn)
/**
* 多例模式Multitcm
* 維護(hù)一個(gè)指定數(shù)量的對象池,當(dāng)請求個(gè)數(shù)超過控制的總數(shù)時(shí),開始循環(huán)重復(fù)使用
*/
public class DuoLi {
private static int num = 4; //對象數(shù)量
private static int count = 1; //對象序號
private static Map<Integer, DuoLi> map = new HashMap();
static {
for (int i=1; i<=num; i++){
map.put(i, new DuoLi());
}
}
private DuoLi(){
}
public static DuoLi getInstance(){
DuoLi duoLi = map.get(count);
System.out.println(String.format("我是第%s個(gè)實(shí)例", count));
count++;
if (count > num){
count = 1;
}
return duoLi;
}
public static void main(String[] args) {
for (int i=0; i<5; i++){
DuoLi duoLi = DuoLi.getInstance();
System.out.println(duoLi.hashCode());
}
}
}
執(zhí)行結(jié)果為
我是第1個(gè)實(shí)例
1558600329
我是第2個(gè)實(shí)例
636718812
我是第3個(gè)實(shí)例
445051633
我是第4個(gè)實(shí)例
1051754451
我是第1個(gè)實(shí)例
1558600329
第五次取的對象為第一個(gè)實(shí)例
剩下的問題
已知類的創(chuàng)建方式有 new,反射、序列化、克隆
在單例模式由于構(gòu)建方法被私有化不可通過new創(chuàng)建,那么反射、序列化、克隆是否可以創(chuàng)建第二個(gè)實(shí)例呢?
https://my.oschina.net/u/3568600/blog/1827375