设计模式之单例模式

来源:互联网 发布:效率最高的查找算法 编辑:程序博客网 时间:2024/05/17 06:03

单例模式,顾名思义一个类有且只有一个实例。
那怎么才能做到一个类只有一个实例呢?
(1)构造私有化
(2)实例静态成员化
(3)通过静态方法获得该实例

单例模式有很多种写法。
1.懒汉式
懒汉式是指需要该实例的时候才创建出该实例。
举个例子:小明下班回到家中,饿到实在不行,于是用烤箱做了一个面包吃。这是懒汉的表现。,不到饿的不行的时候不做饭吃。
下面看代码:

public class Bread{    public static Bread bread; //实例静态成员化    //构造私有化    private Bread() {    }    //静态方法获取实例    public static Bread getBread() {        if (bread == null) {            bread = new Bread(); //需要该实例的时候才创建        }        return bread;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.饿汉式
饿汉式是指在类加载时就初始化好实例。
举个例子:小明下班回到家中,饿的不行,于是赶紧从冰箱中拿出早已做好得面包来吃。这是饿汉的表现,早早的准备好食物,生怕饿着自己。
下面看代码:

public class Bread {    ////实例静态成员化,加载类的时候就初始化好该该实例    private static Bread bread = new Bread();    //构造私有化    private Bread() {    }       //静态方法获取实例    public static Bread getBread() {        return bread;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

下面看Java API用到单例模式饿汉式的一个类:
Runtime类的源码

public class Runtime {    private static Runtime currentRuntime = new Runtime();    public static Runtime getRuntime() {        return currentRuntime;    }    private Runtime() {}    ...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可以看到和上面的Bread类的写法是一样的。

饿汉式是开发中经常写的一种,因为它不容易出错,做的是原子操作(不会被线程调度机制打断的操作),不涉及多线程安全问题。而懒汉式在涉及多线程访问的时候会出错。比如:当线程A执行到if语句中并且还未创建bread对象时,另一个线程B也执行到if语句,这样就会创建两个bread实例。
这个时候菜鸟A问:那怎么改进这种写法呢?
菜鸟B回答:这还不简单,给这个方法加一个同步锁。代码如下:

public class Bread{    public static Bread bread;    private Bread() {    }    public synchronized static Bread getBread() {        if (bread == null) {            bread = new Bread();        }        return bread;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.双重锁定式
这个时候老鸟C听到了菜鸟B的回答,
呵呵道:真是年轻啊!
菜鸟B:怎么了,难道我写的不对?
老鸟C:写的不能说不对,只是你这样一来,每次执行该方法都要受到同步锁的影响,而本意是只有当创建bread实例时才要加同步锁,这样必然导致程序效率下降。于是老鸟露了一手,代码如下:

public class Bread {    private static Bread bread;    private Bread() {    }    public static Bread getBread() {        if (bread == null) {            synchronized (Bread.class) {                if (bread == null) {                    bread = new Bread();                }            }        }        return bread;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这里面有两个if判断,首先第一个if判断是为了当且仅当创建该实例的时候才加同步锁,第二个if是为了防止这样的情况的发生:当线程A执行到第一个if,并进入同步代码块时,线程B也执行到第一个if的位置,因为比线程A慢了一步,于是需要等线程A释放所对象才能继续执行,当线程A实例好bread对象之后,倘若不加第二个if判断,线程B不知道bread是否已经实例化好,于是会紧接着再创建一个Bread实例。这样就会出错。
听完老鸟的讲解,菜鸟们恍然大悟。这就是双重锁定式的单例模式。

4.内部持有类式
这种写法和饿函数相似,都是类加载的时候初始化好实例,只不过这种写法是将初始化操作放在了内部类中。代码如下:

public class Bread {    private Bread() {    }    private static class BreadHolder {        private static Bread bread = new Bread();    }    public static Bread getInstance() {        return BreadHolder.bread;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

总结
单例模式保证了一个类只有一个实例,并提供全局访问该实例的方法。除了第一种写法,其他的写法都保证了多线程安全问题(多个类加载器和反射手段除外,不做考虑)。


0 0
原创粉丝点击