1.定義
確保一個類只有一個實例露筒,并提供一個全局訪問點达箍。
2.實現(xiàn)方式
2.1 聲明即創(chuàng)建對象方式
package com.methon.singleton;
public class SigDemo01 {
private static SigDemo01 INSTANCE=new SigDemo01 ();
private SigDemo01 (){};
public static SigDemo01 getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
SigDemo01 sig01=SigDemo01 .getInstance();
SigDemo01 sig02=SigDemo01 .getInstance();
/* 判斷是否是同一個對象 */
System.out.println(sig01==sig02);
}
}
其中static聲明的靜態(tài)成員INSTANCE只會在類加載的時候創(chuàng)建一次,new一個對象骚烧。
私有的構(gòu)造函數(shù)保證只能在此類中調(diào)用,其他類中想要獲取實例只能調(diào)用getInstance()方法。
返回static對象刻两。保證對象的唯一性赖草。上面的代碼也可以寫成:
package com.methon.singleton;
public class SigDemo02{
private static SigDemo02 INSTANCE;
static {
INSTANCE=new SigDemo02();
}
private SigDemo02(){};
public static SigDemo02 getInstance(){
return INSTANCE;
}
public static void main(String[] args) {
SigDemo02 sig01=SigDemo02.getInstance();
SigDemo02 sig02=SigDemo02.getInstance();
/* 判斷是否是同一個對象 */
System.out.println(sig01==sig02);
}
}
2.2 懶加載方式
以下為懶加載方式的演變,方法逐漸變得趨于完美
2.2.1 錯誤的懶加載寫法(不支持多線程)
package com.methon.singleton;
public class SigDemo03{
private static SigDemo03 INSTANCE;
private SigDemo03(){
}
public static SigDemo03 getInstance(){
if(INSTANCE==null){
INSTANCE=new SigDemo03();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(()-> System.out.println(SigDemo03.getInstance().hashCode())).start();
}
}
}
如果代碼中沒有多線程,則程序沒有任何問題。但存在多線程的情況下,執(zhí)行到if(INSTANCE==null)后乡翅, INSTANCE=new SigDemo03()語句前鳞疲,可能有多個線程通過了if(INSTANCE==null)的判斷,從而不能保證創(chuàng)建出的對象有且僅有1個蠕蚜。
2.2.2 懶加載的正確寫法(加鎖)
package com.methon.singleton;
public class SigDemo04{
private static SigDemo04 INSTANCE;
private SigDemo04() {
}
public static synchronized SigDemo04 getInstance() {
if (INSTANCE == null) {
INSTANCE = new SigDemo04();
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(SigDemo04.getInstance().hashCode())).start();
}
}
}
對比之前的方法尚洽,只是在getInstance()方法上添加了synchronized ,保證了線程安全靶累。但這樣做的話降低了程序的執(zhí)行效率腺毫。
2.2.3 為了解決加鎖后程序效率變低而采取的錯誤處理
package com.methon.singleton;
public class SigDemo05{
private static SigDemo05 INSTANCE;
private SigDemo05() {
}
public static SigDemo05 getInstance() {
if (INSTANCE == null) {
synchronized (SigDemo05.class) {
INSTANCE = new SigDemo05 ();
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(SigDemo05 .getInstance().hashCode())).start();
}
}
}
為了提高效率,在判斷INSTANCE == null之后在進行加鎖的判斷。但是請注意挣柬,雖然進行了加鎖操作拴曲,但是最后線程們都會創(chuàng)建新的對象。
2.2.4 為了解決加鎖后程序效率變低而采取的正確處理
package com.methon.singleton;
public class SigDemo06{
private static SigDemo06 INSTANCE;
private SigDemo06 () {
}
public static SigDemo06 getInstance() {
if (INSTANCE == null) {
synchronized (SigDemo06 .class) {
if (INSTANCE == null) {
INSTANCE = new SigDemo06 ();
}
}
}
return INSTANCE;
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(SigDemo06 .getInstance().hashCode())).start();
}
}
}
此方式雖然有些繁瑣凛忿,但是正確的做法澈灼。在判斷INSTANCE == null后進行加鎖,之后在進行判空操作。仍為空后在進行new新的對象叁熔。避免了只要為空就會創(chuàng)建新的對象委乌。
2.2.5 較完美的靜態(tài)內(nèi)部類方式
package com.methon.singleton;
public class SigDemo07{
private SigDemo07() {
}
public static SigDemo07 getInstance() {
return SigDemo07Holder.INSTANCE;
}
private static class SigDemo07Holder {
private final static SigDemo07 INSTANCE = new SigDemo07();
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(SigDemo07.getInstance().hashCode())).start();
}
}
}
此種方式,避免了聲明即創(chuàng)建對象的方式荣回,采用靜態(tài)內(nèi)部類的方式遭贸,需要時才創(chuàng)建對象。比較完美且代碼不冗余心软。
2.2.6 effictive java給出的方式
package com.methon.singleton;
public enum SigDemo08{
INSTANCE;
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(() -> System.out.println(SigDemo08.INSTANCE.hashCode())).start();
}
}
}
直接設(shè)置成了枚舉類型壕吹,且僅有一個枚舉量INSTANCE。
3.總結(jié)
正常需要單例模式的情況一般都需要創(chuàng)建對象删铃。所以是否進行懶加載區(qū)別相差不大耳贬。故方法2.1滿足平常使用的要求。除此之外猎唁,方法2.2.4, 2.2.5, 2.2.6更為完美咒劲,推薦使用。