beauty_beast

上善若水 厚德载物

学习设计模式之singleton模式

Posted on 2005-08-18 21:09 柳随风 阅读(535) 评论(1)  编辑  收藏
本文只是本人的学习总结,目的希望能和大家一起交流分享,顺便备忘,如有不正确的地方,欢迎指正。

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;
    }


}


这样的定义在单线程应用中时没有问题的,但是如果是多线程访问的话实际就不是单实例了:
举个简单例子:

/*
 * 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

呵呵,单实例变成三实例了,所以我们编码、设计时一定要考虑该对象的调用环境,保险起见,我们可以加上同步,修改原来的实现,将方法加上同步:
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

这样就保证了多线程调用时也只有一个实例。
呵呵,这样就能保证在同一虚拟机上只有一个实例了吗?
如果对象是个可序列化的对象呢?
/*
 * 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()));
    }
}

运行输出如下:
contructor has been inited
false

学习总结如下:
    采用单实例模式时设计编码时要尽可能的多考虑其调用场景,在实现中规避不应该出现的多实例情形。














Feedback

# re: 学习设计模式之singleton模式  回复  更多评论   

2005-08-18 22:03 by Rocky
呵呵,我再补充一点,关于单实例。

版主例子中给了单实例的一种实现:
public class TestClass
{
private static TestClass instance;
private TestClass(){}

public static TestClass()
{
if( null != instance) instance = new TestClass();
return instance;
}
public void testA(){};
public void testB(){};
}

这种是比较常用的,但有这么一个问题。即该类一旦实例化,那么将永远不能被回收。即使你将 instance 赋成 null,也不能将该类清除掉。


还有一种单实例模式,见下:
public class TestClass
{
private String aaa;
public TestClass(){
//初始化单实例中的一些变量
aaa = .....;
}

public static void testA(){
//针对aaa的操作
}

public static void testB(){}{
//针对aaa的操作
}
}
即将方法声明为 static 方法。这样声明之后,那么针对aaa的操作的所有行为也将遵循单实例模式。

这种方式的优点就是类的实例可以随时销毁。但有个缺点:即每次调用都需要创建一个实例出来,有时间上的开销。

只有注册用户登录后才能发表评论。


网站导航: