设计模式复习--单例模式

 

单例模式是一种对象创建型模式,使用单例设计模式可以保证为同一个类只生成唯一的实例对象,也就是说,在整个程序空间中,只存在一个实例对象。

 

单例模式的定义:

保证一个类,只有一个实例存在,同时提供对该实例加以访问的全局访问方法。

 

单例模式的需求场景:

1,多线程之间共享同一资源或者操作同一对象。

2,使用全局变量,共享资源。

3,为了性能的考虑,需要节省对象的创建时间。

 

单例模式的实现:

1,俄汉式。

2,懒汉式。

3,双重检查。

单例模式之懒汉模式:

public class Singleton {
        //利用静态变量来记录Singleton的唯一实例
        private static Singleton uniqueInstance;
        
        /*
         * 构造器私有化,只有Singleton类内才可以调用构造器
         */
        private Singleton(){
                
        }
        
        public static Singleton getInstance(){
                if(uniqueInstance == null){
                        uniqueInstance = new Singleton();
                }
                
                return uniqueInstance;
        }  
}

 

如果有两个线程都要执行这段代码,很有可能会产生两个实例对象

第一、  使用synchronized来处理。也就是说将getInstance()方法变成同步方法即可。
public class Singleton {
        //利用静态变量来记录Singleton的唯一实例
        private static Singleton uniqueInstance;
        
        /*
         * 构造器私有化,只有Singleton类内才可以调用构造器
         */
        private Singleton(){
                
        }
        
      //提供全局静态同步方法 public static synchronized Singleton getInstance(){ if(uniqueInstance == null){ uniqueInstance = new Singleton(); } return uniqueInstance; } }

 第二,单例模式之饿汉式模式--直接初始化静态变量。这样就保证了线程安全:

public class Singleton {
        /*
         * 利用静态变量来记录Singleton的唯一实例
         * 直接初始化静态变量,这样就可以确保线程安全了
         */
        private static Singleton uniqueInstance = new Singleton();
        
        /*
         * 构造器私有化,只有Singleton类内才可以调用构造器
         */
        private Singleton(){
                
        }
        
        public static Singleton getInstance(){
                return uniqueInstance;
        }
}

 第三、  用“双重检查加锁”,在getInstance()中减少使用同步。

public class Singleton {
        /*
         * 利用静态变量来记录Singleton的唯一实例
         * volatile 关键字确保:当uniqueInstance变量被初始化成Singleton实例时,
         * 多个线程正确地处理uniqueInstance变量
         * 
         */
        private volatile static Singleton uniqueInstance;
        
        /*
         * 构造器私有化,只有Singleton类内才可以调用构造器
         */
        private Singleton(){
                
        }
        
        /*
         * 
         * 检查实例,如果不存在,就进入同步区域
         */
        public static Singleton getInstance(){
                if(uniqueInstance == null){
                        synchronized(Singleton.class){    //进入同步区域
                                if(uniqueInstance == null){     //在检查一次,如果为null,则创建
                                        uniqueInstance  = new Singleton();
                                }
                        }
                }
                
                return uniqueInstance;
        }  
}

在这里是首先检查是否实例已经创建了,如果尚未创建,才会进行同步。这样一来。只有第一次会同步。

 单例设计模式的优点和缺点:

优点:

1,减少对象创建,节省系统资源,提高系统性能。

缺点:

1,不利于扩展。

在android开发中使用的场景:

源码里面:详见:http://www.cnblogs.com/yemeishu/archive/2013/01/04/2843705.html

说下App场景下单利常用的场景:

如下是我以前项目里面使用ormlite数据时做的一个数据库管理的类,通过DBManager.getInstance(context)获取到单利的dbmanager对象,从而进一步获取数据库操作层的dao对象,以及全局清理数据库里面内容的操作:

/**
 * 数据库管理类 
 */
public class DBManager {
    private OrmLiteSqliteOpenHelper helper;
    private static DBManager instance;

    private DBManager(Context context) {
        helper = OpenHelperManager.getHelper(context, DBHelper.class);
    }

    /**
     * 获取该类实例
     * 
     * @param context
     *            上下文
     * @return
     */
    public synchronized static DBManager getInstance(Context context) {
        if (instance == null) {
            instance = new DBManager(context);
        }
        return instance;
    }

    /**
     * 获取对应数据表的dao
     * 
     * @param clazz
     *            表对应的Bean
     * @return
     */
    public <T, ID> Dao<T, ID> getDao(Class<T> clazz) {
        try {
            return helper.getDao(clazz);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 清除过期的数据
     * 
     * @param tableName
     *            表名
     * @param value
     *            移动天数(0当天,-1向前一天)
     */
    public void clear(String tableName, int value) {
        SQLiteDatabase db = helper.getWritableDatabase();
        try {
            long date = DateUtil.getTimeInMillis(value);
            String[] args = { String.valueOf(date) };
            db.delete(tableName, "updateTime" + "<?", args);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // db.close();
        }
    }

    /**
     * 清除给定表中的所有数据
     */
    public void clearAll(String tableName) {
        SQLiteDatabase db = helper.getWritableDatabase();
        db.delete(tableName, null, null);
    }

    public OrmLiteSqliteOpenHelper getHelper() {
        return helper;
    }

    /**
     * 清除7天前的数据
     * 
     * @param tableName
     */
    public void clear(String tableName) {
        clear(tableName, -7);
    }

    /**
     * 释放资源
     */
    public void release() {
        OpenHelperManager.releaseHelper();
    }

}

 


 

2015年11月30日23:30:56更新

最近在读Mr.simple的设计模式的书,书中讲到了除了以上的集中方式以外的实现单利模式的几种实现:

1,静态内部类单利模式(比较推荐的实现模式)

  Java中静态内部类可以访问其外部类的成员属性和方法,同时,静态内部类只有当被调用的时候才开始首次被加载,保证线程安全,保证单利对象的唯一性,延迟单利实例化。

public class Singleton
{
    private Singleton(){ }

    public static Singleton getInstance()
    {
        return Singletonholder.instance;        
    }

    //在第一次被引用时被加载
    static class Singletonholder
    {
        private static Singleton instance = new Singleton();
    }

    public static void main(String args[])
    {
        Singleton instance = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        System.out.println(instance == instance2);
    }
}

结果为:true

2,通过枚举的方式来实现单利

不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。但是失去了类的一些特性,没有延迟加载,枚举是线程安全的

public enum Singleton{
    INSTANCE;

    String getMessage(){
        return "you are doubi";
    }

    public static void main(String[] args) {
        String message = Singleton.INSTANCE.getMessage();
        System.out.println(message);
    }
}

输出结果:

you are doubi

通过枚举实现的相关讨论:http://segmentfault.com/q/1010000000646806

3,使用容器实现单利模式

 一般是通过一个专门的类对各单例模式的此单一实例进行管理和维护。通过Map方式可以方便的实现此中目的

首先复习下map相关知识点:

一个映射不能包含重复的键,每个键最多只能映射到一个值

Map的键唯一,因此如下className是唯一的,如果map中不存在,就实例化对象,并放入map集合(这个原理好骚,哈哈哈)

public class MySingleTonManager {

    private static Map singleTonMap = new HashMap();

    public static void main(String[] args) {
        // 获取A类的单例
        A a = (A) getInstance(A.class.getName());
        // 获取B类的单例
        B b = (B) getInstance(B.class.getName());
    }

    // 根据类型获取单例
    public static Object getInstance(String className) {
        // 判断singleTonMap中是否有此单例,有则取得后返回,无则添加单例后返回
        if (!singleTonMap.containsKey(className)) {
            try {
                singleTonMap.put(className, Class.forName(className).newInstance());
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return singleTonMap.get(className);
    }
}

class A {

}

class B {

}

 注意点:

传递给单利对象的context对象最好是Application Context。

 

 

posted @ 2014-04-21 21:40  西北野狼  阅读(434)  评论(0编辑  收藏  举报