本文只是本人的學習總結,目的希望能和大家一起交流分享,順便備忘,如有不正確的地方,歡迎指正。
singleton模式我們在開發時經常會使用到,比如將一個系統運行時的初始配置數據封裝成一個配置對象,在系統初始化時實例化該對象,因為對于整個系統運行時該對象的成員都是不變的,如果只需要一個實例就行,這樣對系統的性能是很有益的。往往該配置對象都是和資源密切相關的(例如 數據庫連接、文件等等),但是如果采用該模式設計、編碼不當,常會造成資源泄漏,甚至更嚴重的問題(我的最近一個項目就出現過問題)。
一個簡單的單實例模式實現如下:

/**//*
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;




/**//**
* @author liusuifeng
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/

public class TestSingleTon
{
private static TestSingleTon test = null;


private TestSingleTon()
{

System.out.println("contructor has been inited");
}


public static synchronized TestSingleTon getInstance()
{

if (test == null)
{

test = new TestSingleTon();

}
return test;
}

}
這樣的定義在單線程應用中時沒有問題的,但是如果是多線程訪問的話實際就不是單實例了:
舉個簡單例子:
運行時系統輸出如下:
TestSingleTon====study.design.singleton.TestSingleTon@107077e
TestSingleTon====study.design.singleton.TestSingleTon@7ced01
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
呵呵,單實例變成三實例了,所以我們編碼、設計時一定要考慮該對象的調用環境,保險起見,我們可以加上同步,修改原來的實現,將方法加上同步:

public static synchronized TestSingleTon getInstance()
{

if (test == null)
{

test = new TestSingleTon();

}
return test;
}
執行測試代碼輸出:
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
這樣就保證了多線程調用時也只有一個實例。
呵呵,這樣就能保證在同一虛擬機上只有一個實例了嗎?
如果對象是個可序列化的對象呢?
下面的方法就又偷出一個實例出來:
運行輸出如下:
contructor has been inited
false
學習總結如下:
采用單實例模式時設計編碼時要盡可能的多考慮其調用場景,在實現中規避不應該出現的多實例情形。
singleton模式我們在開發時經常會使用到,比如將一個系統運行時的初始配置數據封裝成一個配置對象,在系統初始化時實例化該對象,因為對于整個系統運行時該對象的成員都是不變的,如果只需要一個實例就行,這樣對系統的性能是很有益的。往往該配置對象都是和資源密切相關的(例如 數據庫連接、文件等等),但是如果采用該模式設計、編碼不當,常會造成資源泄漏,甚至更嚴重的問題(我的最近一個項目就出現過問題)。
一個簡單的單實例模式實現如下:












































這樣的定義在單線程應用中時沒有問題的,但是如果是多線程訪問的話實際就不是單實例了:
舉個簡單例子:
/*
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;
/**
* @author liusuifeng
*/
public class TestThread extends Thread {
public void run(){
TestSingleTon test=TestSingleTon.getInstance();
System.out.println("TestSingleTon===="+test);
}
public static void main(String[] args){
for(int i=0;i<10;i++){
TestThread mthread=new TestThread();
mthread.start();
}
}
}
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;
/**
* @author liusuifeng
*/
public class TestThread extends Thread {
public void run(){
TestSingleTon test=TestSingleTon.getInstance();
System.out.println("TestSingleTon===="+test);
}
public static void main(String[] args){
for(int i=0;i<10;i++){
TestThread mthread=new TestThread();
mthread.start();
}
}
}
運行時系統輸出如下:
TestSingleTon====study.design.singleton.TestSingleTon@107077e
TestSingleTon====study.design.singleton.TestSingleTon@7ced01
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
TestSingleTon====study.design.singleton.TestSingleTon@1ac04e8
呵呵,單實例變成三實例了,所以我們編碼、設計時一定要考慮該對象的調用環境,保險起見,我們可以加上同步,修改原來的實現,將方法加上同步:












執行測試代碼輸出:
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
TestSingleTon====study.design.singleton.TestSingleTon@11a698a
這樣就保證了多線程調用時也只有一個實例。
呵呵,這樣就能保證在同一虛擬機上只有一個實例了嗎?
如果對象是個可序列化的對象呢?
/*
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;
import java.io.Serializable;
/**
* @author liusuifeng
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class TestSingleTon implements Serializable {
private static TestSingleTon test = null;
private TestSingleTon() {
System.out.println("contructor has been inited");
}
public static synchronized TestSingleTon getInstance() {
if (test == null) {
test = new TestSingleTon();
}
return test;
}
}
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;
import java.io.Serializable;
/**
* @author liusuifeng
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class TestSingleTon implements Serializable {
private static TestSingleTon test = null;
private TestSingleTon() {
System.out.println("contructor has been inited");
}
public static synchronized TestSingleTon getInstance() {
if (test == null) {
test = new TestSingleTon();
}
return test;
}
}
下面的方法就又偷出一個實例出來:
/*
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @author liusuifeng
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class TestSerialObject {
private void writeObject() {
TestSingleTon tempobj = TestSingleTon.getInstance();
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(
new FileOutputStream("c:\\object.data"));
oos.writeObject(tempobj);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
finally{
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public Object getSerialObject() {
Object o = new Object();
ObjectInputStream ois =null;
writeObject();
try {
ois = new ObjectInputStream(new FileInputStream(
"c:\\object.data"));
o=ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally{
try {
ois.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return o;
}
public static void main(String[] args) {
TestSerialObject to=new TestSerialObject();
Object obj1=to.getSerialObject();
System.out.println(obj1.equals(TestSingleTon.getInstance()));
}
}
* Created on 2005-8-18
*
* TODO To change the template for this generated file go to
* Window - Preferences - Java - Code Style - Code Templates
*/
package study.design.singleton;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* @author liusuifeng
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class TestSerialObject {
private void writeObject() {
TestSingleTon tempobj = TestSingleTon.getInstance();
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(
new FileOutputStream("c:\\object.data"));
oos.writeObject(tempobj);
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
finally{
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public Object getSerialObject() {
Object o = new Object();
ObjectInputStream ois =null;
writeObject();
try {
ois = new ObjectInputStream(new FileInputStream(
"c:\\object.data"));
o=ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
finally{
try {
ois.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
return o;
}
public static void main(String[] args) {
TestSerialObject to=new TestSerialObject();
Object obj1=to.getSerialObject();
System.out.println(obj1.equals(TestSingleTon.getInstance()));
}
}
運行輸出如下:
contructor has been inited
false
學習總結如下:
采用單實例模式時設計編碼時要盡可能的多考慮其調用場景,在實現中規避不應該出現的多實例情形。