设计模式之单例模式

来源:互联网 发布:路由器ip绑定mac地址 编辑:程序博客网 时间:2024/06/08 01:25

什么时候采用单例模式:

当某个类只需要一个实例的时候,比如一台电脑 可以连接多个打印机,但是一个时间段只能有一个打印服务。

单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且他可以提供一个访问该实例的办法。


-:表示private   +:表示public

1. 单例类(此处的单例类是懒汉式单例类,其处理方式是在第一次被引用时,才会将自己实例化,所以被称为懒汉式单例类)

public class Singleton {private static Singleton instance;//构造方法让其private,这就堵死了外界利用new创建此类实例的可能private Singleton(){ }//此方法是获得本类实例的唯一全局访问点public static Singleton GetInstance(){//若实例不存在,则new一个新实例,否则返回已有的实例if(instance==null){instance=new Singleton();}return instance;}}


客户端代码:

public class Main {public static void main(String[] args) {// TODO Auto-generated method stubSingleton s1=Singleton.GetInstance();Singleton s2=Singleton.GetInstance();if(s1==s2){System.out.println("两个对象是相同的实例");}elseSystem.out.println("两个对象不是相同的实例");}}


运行结果:

说明:单例模式保证了唯一的实例。

2. 多线程时的单例

认真思考会发现,上面的例子存在问题,比如,在多线程的程序中,多个线程同时访问Singleton类,调用GetInstance()方法,会有可能造成创建多个实例。

解决办法:加锁,在GetInstance 方法上加上synchronized 。所有这个类的加了 synchronized 的方法,在执行时,会获得一个该类的唯一的同步锁,当这个锁被占用时,其他的加了 synchronized 的方法就必须等待

public static synchronized Singleton GetInstance(){//若实例不存在,则new一个新实例,否则返回已有的实例if(instance==null){instance=new Singleton();}return instance;}

这段代码使得对象实例由最先进入的那个线程创建,以后的线程在进入时不会再去创建对象实例。这就保证了多线程环境下的同时访问也不会造成多个实例的生成。

但是它仍然存在问题:每次调用GetInstance 方法都会加锁,这样会影响性能。同步方法是对方法的整体进行加锁,对运行效率来讲很不利,因此考虑采用同步代码块。

改进办法:双重锁定

3.双重锁定

双重锁定:不用让线程每次都加锁,而是在实例被创建的时候再加锁处理。同时也保证了多线程的安全。这种做法被称为Double-Check Locking (双重锁定)

public static Singleton GetInstance(){if(instance==null){synchronized(Singleton.class){if(instance==null){instance=new Singleton();}}}return instance;}

那为什么上面在同步里面还要做一次instance实例是否存在的判断呢?

因为当instance为null时,并且存在同时有两个线程调用GetInstance 方法时,它都将可以通过第一重instance的判断,然后由于同步机制,这两个线程只有一个可以进入,另一个在外排队等候,必须要其中一个进入并出来后,另一个才能进入。而此时如果没有第二重的instance是否为null的判断,则第一个线程创建了实例,而第二个线程还是可以继续创建新的实例,这就没有达到单例的目的。

4.饿汉式单例类

饿汉式单例类:采用以下静态初始化方式在自己被加载时就将自己实例化,所以被形象地称为饿汉式单例类

public class Singleton2 {    private static final Singleton2 instance=new Singleton2();private Singleton2(){ }//此方法是获得本类实例的唯一全局访问点public static Singleton2 GetInstance(){return instance;}}

5.静态内部类实现单例模式

懒汉模式考虑线程安全需要在获取单例的方法添加synchronized关键字实现同步代码块,这样造成了性能损耗;而饿汉模式不能延迟实例化对象,下面是一个静态内部类单例模式的实现,既保证了线程的安全,有能够延迟加载,也就是在第一次使用的时候加载。

public class Singleton3 {private Singleton3(){}public static Singleton3 GetInstance(){return Singleton3Handler.instance;}private static class Singleton3Handler{private static final Singleton3 instance = new Singleton3();}}

6.三者对比

1.由于饿汉式,即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。

2.懒汉式会面临多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。

3.静态内部类单例模式的实现,既保证了线程的安全,有能够延迟加载

4.到底使用哪一种方式,取决于实际的需求。

测试代码:

public class MyThread extends Thread{/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubMyThread t1=new MyThread();MyThread t2=new MyThread();MyThread t3=new MyThread();t1.start();t2.start();t3.start();}@Overridepublic void run() {for(int i=0;i<5;i++){System.out.println(Singleton.GetInstance().hashCode());}}}


参考资料:

1.《大话设计模式》 程杰

2.http://blog.csdn.net/jason0539/article/details/23297037




0 0
原创粉丝点击