Java学习笔记-单件模式

来源于headfirst设计模式

单例模式的性能指标:lazy(是否延迟实例化),线程安全

一、延迟实例化,同步(synchronized)方法-->由于是单例更需要考虑处理问题需要考虑的同步问题,延迟实例化主要是面向资源敏感的对象

 1 public class Singleton {
 2 
 3     private static Singleton uniqueinstance;//唯一的实例变量
 4 
 5     private Singleton() {//私有的初始化方法
 6     }
 7 
 8     public static synchronized Singleton getInstance() {
 9     //实例的获取方法,将普通的初始化方法变为获得方法
10         if (uniqueinstance== null) {
11             uniqueinstance= new Singleton();//直接在类内部初始化
12         }
13         return instance;
14     }
15 }

 

二、直接实例化(静态工厂方法)

 1 public class Singleton {
 2 
 3     private static Singleton uniqueInstance = new Singleton();
 4     //直接创建单例
 5     private Singleton() {
 6     }
 7 
 8     public static Singleton getInstance() {
 9 
10         return uniqueInstance;
11     }
12 }

 

三、双重检验加锁,双重检查锁定模式首先验证锁定条件(第一次检查),只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)。-->用来减少并发系统中竞争和同步的开销。[http://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F]

public class Singleton {

    private volatile static Singleton instance = null;//volatile变量

    private Singleton() {
    }

    public static Singleton getInstance() {

        if (instance == null) {//只有首次初始化时才用到了同步模块,避免了同步对资源的浪费
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile变量的作用[参考:http://blog.csdn.net/orzorz/article/details/4319055]

用在多线程,为了同步变量。在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。 
一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。synchronized的优势在于其可以把方法或者代码块变为原子操作,而volatile无法。

 四、枚举单例,上面的方法都有一个问题就是反序列化,破坏了单例的特性,你可以去查询使用readResolve(),但比较麻烦,推荐使用Java的这个新特性——枚举类型。它具有内置的序列化支持和线程安全支持。

public enum EasySingleton{

  INSTANCE;
}

在 java 垃圾回收中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。

[http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html]

五、各种优化

/**
 * 基于内部类的单例模式  Lazy  线程安全
 * 优点:
 * 1、线程安全
 * 2、lazy
 * 缺点:
 * 1、待发现
 * 
 * @author laichendong
 * @since 2011-12-5
 */
public class SingletonFive {
    
    /**
     * 内部类,用于实现lzay机制
*/
    private static class SingletonHolder{
        /** 单例变量  */
        private static SingletonFive instance = new SingletonFive();
    }
    
    /**
     * 私有化的构造方法,保证外部的类不能通过构造器来实例化。
*/
    private SingletonFive() {
        
    }
    
    /**
     * 获取单例对象实例
     * 
     * @return 单例对象
*/
    public static SingletonFive getInstance() {
        return SingletonHolder.instance;
    }
    
}

http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html

这种方式利用静态内部类的优势,既是后实例化的又是线程安全的。

 

引申阅读:http://jiangzhengjun.iteye.com/blog/652440

http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

posted @ 2014-02-18 09:23  kcrosswind  阅读(230)  评论(0编辑  收藏  举报