1 簡介
單例模式是一種對象創(chuàng)建型模式静稻。單例模式保證一個類只有一個實例存在,并且同時提供一個全局的方法進(jìn)行進(jìn)行訪問該實例匈辱。
應(yīng)用場景
在多個線程之間共享或?qū)ν粋€對象進(jìn)行操作時
用作全局變量時
大規(guī)模系統(tǒng)中為了提高性能振湾,減少對象的創(chuàng)建,節(jié)省創(chuàng)建時間
2 幾種實現(xiàn)方式
2.1 餓漢式
所謂餓漢式亡脸,就是不管三七二十一押搪,一上來就開干。不管你需不需要創(chuàng)建某個對象浅碾,只要在加載類的時候就創(chuàng)建大州。
** - 實現(xiàn)方式**
實體類:Person.class
public class Person {
private String name;
private Integer age;
<!-在Person被加載的時候Person對象就被創(chuàng)建-!>
private static Person person = new Person();
<!--使用private ,防止用戶利用空參構(gòu)造器進(jìn)行實例化--!>
private Person(){
}
public static Person getPerson(){
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
MainClass.class
public class MainClass {
public static void main(String[] args) {
Person person = Person.getPerson();
person.setAge(20);
person.setName("HAH");
System.out.println(person.getName());
}
}
在類加載時就實例化對象垂谢,這樣可以保證該對象的唯一性厦画,是線程安全的,但是相對的效率可能會降低滥朱,因為用戶也許就不需要實例化該對象根暑。
2.2 懶漢式
所謂懶漢式,顧名思義徙邻,就是用到的時候在創(chuàng)建购裙。也就是說,在類加載的時候不會實例化對象鹃栽,只有當(dāng)用戶真正的要創(chuàng)建對象的時候再調(diào)用方法實例化對象躏率。
- 實現(xiàn)方式
Peison.class
public class Person {
private String name;
private Integer age;
<!--類加載時不會實例化對象--!>
private static Person person = null;
private Person(){
}
<!-用戶第一次調(diào)用getPerson時會創(chuàng)建對象,以后都不需要創(chuàng)建-!>
public static Person getPerson(){
if(person == null){
person = new Person();
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
可見民鼓,使用懶漢式可以提高程序的靈活性薇芝。但是這樣也存在一個很大的缺陷。當(dāng)?shù)谝淮握{(diào)用getPerson( )
方法時有多個線程同時訪問時丰嘉,那么就會出現(xiàn)線程同步問題夯到。所以的我們需要在方法上加上synchronized
防止多個線程同時訪問。
** - 改進(jìn)**
Person.class
public class Person {
private String name;
private Integer age;
private static Person person = null;
private Person(){
}
<!-在方法上加上synchronized保證當(dāng)前方法同一時間點只能有一個線程訪問-!>
public static synchronized Person getPerson(){
if(person == null){
person = new Person();
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
在給getPerson( )
方法上加上synchronized
可以避免多個線程第一次同時訪問實例化多個對象饮亏,但是這樣在方法上加上synchronized
會大大的降低程序的效率耍贾,因為只有第一次調(diào)用getPerson( )
方法時才會發(fā)生線程問題阅爽,而第二次及以后調(diào)用均不會發(fā)生線程問題,這樣當(dāng)有一個線程調(diào)用getPerson( )
方法時荐开,其它線程都將被阻塞付翁,直到調(diào)用方法的線程調(diào)用完畢。
2.3 雙重檢查
雙重檢查既是在懶漢式的改進(jìn)基礎(chǔ)上進(jìn)行改進(jìn)晃听,對實例化對象的代碼加鎖并加以判斷百侧,從而實現(xiàn)線程安全并且和懶漢式相比效率也大大提高。
- 具體實現(xiàn)
Person.class
public class Person {
private String name;
private Integer age;
private static Person person = null;
private Person(){
}
public static Person getPerson(){
if(person == null){ (1
<!--將實例化對象的代碼加鎖--!>
synchronized(Person.class){ (2
<!-防止第一次調(diào)用getPerson方法時多個線程同時進(jìn)入 (1能扒,當(dāng)?shù)谝痪€程創(chuàng)建完對象釋放鎖后佣渴,
后面的線程獲得鎖多次創(chuàng)建對象-!>
if(person == null){
person = new Person();
}
}
}
return person;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
3 三種實現(xiàn)方式的比較
餓漢式和雙重檢查是線程安全的,而未改進(jìn)的懶漢式不是線程安全的初斑。
改進(jìn)后的懶漢式效率比較低辛润,因為給方法加上了
synchronized
,導(dǎo)致了同一時刻只能有一個線程訪問該方法见秤,造成其它線程堵塞砂竖。雙重檢查相對于餓漢式來說靈活性更高,只有用戶用到該對象的時候才會實例化秦叛,相對于懶漢式效率更高晦溪,用戶只有第一次訪問該瀑粥,方法才會進(jìn)入鎖挣跋,其它線程可能會堵塞,但是以后每次方法都可以并發(fā)狞换,線程不會再被阻塞避咆。
最近剛學(xué)設(shè)計模式,哪理解錯了修噪,望各位濕兄大佬多多指正