您的位置:首页 > 其它

设计模式——单例模式

2017-01-17 13:27 197 查看
作为开发者的我们,想必设计模式对大家来说并不陌生,设计模式共有23种,分别是:工厂方法(FactoryMethod)、抽象工厂(AbstractFactory) 、建造者模式(Builder)、单态模式(Singleton)、 原型模式(Prototype)、 适配器模式(Adapter)、桥接模式(Bridge) 、组合模式(Composite) 、装饰模式(Decorator)、外观模式(Facade)、 享元模式(Flyweight)、代理模式(Proxy)、责任链模式(Chain of Responsibility)、 命令模式(Command)、 解释器模式(Interpreter) 、迭代器模式(Iterator)、中介者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy) 、模板方法(TemplateMethod)、访问者模式(Visitor)。这23种设计模式又分为三类,分别是:创建型模式、结构型模式和行为型模式。创建型模式分别包含的是工厂方法(FactoryMethod)、抽象工厂(AbstractFactory) 、建造者模式(Builder)、单态模式(Singleton)、 原型模式(Prototype)。结构型模式分别包含的是: 适配器模式(Adapter)、桥接模式(Bridge) 、组合模式(Composite) 、装饰模式(Decorator)、外观模式(Facade)、 享元模式(Flyweight)、代理模式(Proxy)。行为型模式分别包含的是:责任链模式(Chain of Responsibility)、 命令模式(Command)、 解释器模式(Interpreter) 、迭代器模式(Iterator)、中介者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy) 、模板方法(TemplateMethod)、访问者模式(Visitor)。

了解了以上还远远不够,下面我们就来了解一下23种设计模式之一的单例模式。单例模式对我们开发者来说是再熟悉不过了。单例模式又分为饿汉式(线程安全,调用效率高。但是不能延时加载)、懒汉式(线程安全,调用效率低。可以延时加载)、双重检测锁(由于此模式偶尔会出现问题,不建议使用)、静态内部类式(线程安全,调用效率高。可以延时加载)、枚举单例(线程安全,调用效率高。不能延时加载)。单例模式的优点是:减少系统开销。我们经常使用的是饿汉式和懒汉式这两种。我们先来说一下饿汉式,饿这个字我想大家都知道啥意思,举个例子:当你很饿的时候你会去挑选你喜欢的食物吗?当然不会。当你很饿的时候,恰好发现有一些食物,此时的你不管是否是自己喜欢的食物,只要能充饥你都会去吃的。饿汉式模式也是一样,不论是否使用,先创建一个实例再说。缺点就是如果只加载本类,而不去调用getSingleton()这个方法的话,将会造成资源的浪费。因为一开始上来不论分说的先创建实例,这样很容易造成资源浪费。

public class Singleton {
private static Singleton singleton = new Singleton();

public Singleton() {
}

public static Singleton getSingleton() {
return singleton;
}
}


懒汉式则就不同了,懒汉式就好比你不是很饿,此时的你很困正在床上休息,不论旁边的人如何喊你吃东西,只要不是自己特别喜欢吃的,你都懒得起来去吃,当听到有你喜欢吃的东西的时候此时的你可能会去品尝一下。懒汉式就是这样,不调用就不实例化。虽然解决了资源的浪费,但是由于每次调用getSingleton() 时都要同步,造成并发效率特别低。也许有人会说我不会不加synchronized 这个关键字进行同步限制吗?但是这样做是不行的,如果并发量有点高的情况下容易造成多次实例化,比如说线程1和线程2调用了getSingleton()这个方法,当刚调用还没有来得及判断singleton是否为空是,此时线程1挂起了,然后当线程2执行到singleton = new Singleton2()时,线程2挂起,此时线程1又从刚刚挂起的地方执行,这样就导致被实例化了两次。如果加了synchronized 以后,即使线程1挂起,线程2也不会去执行,直到线程1执行完,线程2才会去执行。

public class Singleton2 {
private static Singleton2 singleton;

public Singleton2() {
}

public static synchronized Singleton2 getSingleton() {
if (singleton==null){
singleton = new Singleton2();
}
return singleton;
}
}


双重检测锁,顾名思义就是多次检测,这个模式将同步内容放到if里面,这样做提高了执行效率,没有必要每次获取对象时都要进行同步,只有第一次才会同步,以后每次都不需要。由于编译器优化问题此模式不建议使用。

public class Singleton3 {
private static Singleton3 singleton=null;

public Singleton3() {
}

public static Singleton3 getSingleton() {
if (singleton==null){
Singleton3 st;
synchronized (Singleton3.class){
st = singleton;
if (st==null){
synchronized (Singleton3.class){
if (st==null){
st = new Singleton3();
}
}
singleton = st;
}
}
}
return singleton;
}
}


静态内部类模式,里面会用到final 这个关键字,static final 的作用就是防止被改变。这样获取到的空间地址永远是不会改变的,这样就会保证只有一个实例存在,而且是线程安全的。具备有高并发和延时加载的优势。

public class Singleton4 {
private static class SingletonDemo{
private static final Singleton4 singleton = new Singleton4();
}

public Singleton4() {
}

public static Singleton4 getSingleton() {
return SingletonDemo.singleton;
}
}


枚举单例,枚举本身就是单例,缺点就是没有延时加载。

public enum  Singleton5 {
SINGLETON;
}


基于以上五种单例模式我们应该如何选择呢?如果是单例对象,占用资源较少,而且又不需要延时加载的话枚举式好于饿汉式。如果是单例对象,占用资源较大,而且需要延时加载的话静态内部类好于懒汉式。单例模式就到此结束了,平时我们使用的Application里面就涉及有单例模式。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息