码迷,mamicode.com
首页 > 编程语言 > 详细

Java 设计模式---单例模式

时间:2015-07-26 12:29:06      阅读:112      评论:0      收藏:0      [点我收藏+]

标签:

单例模式的写法


1. 懒汉模式(线程不安全)

懒汉模式:顾名思义就是需要使用的时候才实例化;

线程不安全:设想两个线程同时都运行到判断instance是否等于null的if语句,并且instance的确没有被创建,那么这两个线程都会创建一个实例(执行:instance = new Singleton1),此时就不满足单例模式的要求。

package Singleton;

public class Singleton1 {

    private Singleton1(){}//private仅内部函数可以调用
    
     private static Singleton1 instance = null;
    
    public static Singleton1 getInstance(){
        if(instance == null){
            instance = new Singleton1();//构造函数声明为private,仅内部函数可以调用,从而确保只创建一个实例
         }
        return instance;
    }
}

 

2. 懒汉模式(线程安全)

线程安全:加上同步锁(synchronized)保证线程安全。此时若两个线程同时创建一个实例,由于在一个时刻只有一个线程能获得同步锁,当一个线程加上同步锁时,另一个线程只能等待。当第一个线程发现还没有创建实例时它创建一个实例。接着第一个线程释放同步锁,此时第二个线程可以加上同步锁,但发现此时已经创建过一个实例,就不会重复创建一个实例。从而保证单例模式。

package Singleton;

public class Singleton2 {
    private Singleton2() {}// private仅内部函数可以调用

    private static Singleton2 instance;

    public static synchronized Singleton2 getInstance() {//加同步锁,解决线程同步问题
        if (instance == null) {
            instance = new Singleton2();// 构造函数声明为private,仅内部函数可以调用,从而确保只创建一个实例
        }
       return instance;
    }
}

 

3. 懒汉模式(双重校验锁)

考虑第2种方法由于加锁是一个耗时的操作,所以我们想办法减少加锁的次数。

本方法:如果实例已经被创建,直接返回即可;如果实例没有被创建,此时加锁保证线程安全再创建实例。

注意:关键字volatile

Volatile的作用强制每次都直接读内存,阻止重排序,确保 voltile 类型的值一旦被写入缓存必定会被立即更新到主存

因为:在JAVA多线程环境下,每个Java线程除了共享的虚拟机栈外和Java堆之外,还存在一个独立私有的堆内存(默认情况下大小为512KB,在线程被创建时分配,可以通过-Xss选项调节其默认值大小)。每个线程独立运行,彼此之间都不可见,线程的私有堆内存中保留了一份主内存的拷贝,只有在特定需求的情况下才会与主存做交互(复制/刷新)。此时就会出现一种情况,虽然在某个线程私有的堆内存上已经创建出一个实例,由于线程还没有与主内存进行交互,导致主内存上还没有这个实例。此时用volatile就可以解决这个问题。

package Singleton;

public class Singleton3 {
    private Singleton3() {}// private仅内部函数可以调用

    private volatile static Singleton3 instance = null;

    public static synchronized Singleton3 getInstance() {//加同步锁,解决线程同步问题
        if (instance == null) {//加锁耗时,减少加锁的次数
            synchronized (Singleton3.class){
                if (instance == null) {
                    instance = new Singleton3();// 构造函数声明为private,仅内部函数可以调用,从而确保只创建一个实例
                }
            }
        }
        return instance;
    }
}

4. 饿汉模式

饿汉模式: 相对于懒汉模式,饿汉模式就是在类加载的时候就创建实例。

这种方式基于classloder机制避免了多线程的同步问题,instance在类装载时就实例化。下面就是饿汉模式的两种写法。

package Singleton;

public class Singleton4 {
    private static Singleton4 instance = new Singleton4();  
    private Singleton4 (){}
    public static Singleton4 getInstance() {  
         return instance;  
    }  
}

 

package Singleton;

public class Singleton5 {
     private static Singleton5 instance = null;  
     static {  
          instance = new Singleton5();  
     }  
     private Singleton5 (){}
     public static Singleton5 getInstance() {  
         return Singleton5.instance;  
     }  
}

 

5. 静态内部类

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟饿汉模式不同的是:饿汉模式只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比饿汉模式就显得很合理。

package Singleton;

public class Singleton6 {
    private static class SingletonHolder {
        private static final Singleton6 INSTANCE = new Singleton6();
    }

    private Singleton6() {}

    public static final Singleton6 getInstance() {
        return SingletonHolder.INSTANCE;//单例模式推迟到SingletonHolder这个类加载
    }
}

 

6. 枚举

枚举:它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

package Singleton;

public enum Singleton7 {
    INSTANCE;  
}

 

参考资料


  1. 《Think in Java》
  2. 《Head first design pattern》
  3. 《剑指offer》
  4. http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
  5. http://www.tuicool.com/articles/uyEvaeQ

Java 设计模式---单例模式

标签:

原文地址:http://www.cnblogs.com/ChinaZT/p/4677302.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!