博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式(10)-----创建型模式----- 单例设计模式
阅读量:7281 次
发布时间:2019-06-30

本文共 4143 字,大约阅读时间需要 13 分钟。

 

假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情请点击

 

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

 

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

 

注意:

 

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

 

 

介绍

 

意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

 

主要解决:一个全局使用的类频繁地创建与销毁。

 

何时使用:当您想控制实例数目,节省系统资源的时候。

 

如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

 

关键代码:构造函数是私有的。

 

应用实例: 1、一个党只能有一个书记。 2、Windows 是多进程多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。 3、一些设备管理器常常设计为单例模式,比如一个电脑有两台打印机,在输出的时候就要处理不能两台打印机打印同一个文件。

 

优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。

 

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

 

使用场景: 1、要求生产唯一序列号。 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

 

注意事项:getInstance() 方法中需要使用同步锁 synchronized (Singleton.class) 防止多线程同时进入造成 instance 被多次实例化。

 

由于这个比较加单,所以就不按照书上来了,在网上找几个案例来了解一下。

1、饿汉式(静态常量)[可用]

public class Singleton {

private final static Singleton INSTANCE = new Singleton();

private Singleton(){}

public static Singleton getInstance(){

return INSTANCE;

}

}

优点:这种写法比较简单,就是在类装载的时候就完成实例化。避免了线程同步问题。

缺点:在类装载的时候就完成实例化,没有达到Lazy Loading的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。

2、饿汉式(静态代码块)[可用]

public class Singleton {

private static Singleton instance;

static {

instance = new Singleton();

}

private Singleton() {}

public Singleton getInstance() {

return instance;

}

}

这种方式和上面的方式其实类似,只不过将类实例化的过程放在了静态代码块中,也是在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点和上面是一样的。

3、懒汉式(线程不安全)[不可用]

public class Singleton {

private static Singleton singleton;

private Singleton() {}

public static Singleton getInstance() {

if (singleton == null) {

singleton = new Singleton();

}

return singleton;

}

}

这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。

4、懒汉式(线程安全,同步方法)[不推荐用]

public class Singleton {

private static Singleton singleton;

private Singleton() {}

public static synchronized Singleton getInstance() {

if (singleton == null) {

singleton = new Singleton();

}

return singleton;

}

}

解决上面第三种实现方式的线程不安全问题,做个线程同步就可以了,于是就对 getInstance()方法进行了线程同步。

缺点:效率太低了,每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。方法进行同步效率太低要改进。

5、懒汉式(线程安全,同步代码块)[不可用]

public class Singleton {

private static Singleton singleton;

private Singleton() {}

public static Singleton getInstance() {

if (singleton == null) {

synchronized (Singleton.class) {

singleton = new Singleton();

}

}

return singleton;

}

}

由于第四种实现方式同步效率太低,所以摒弃同步方法,改为同步产生实例化的的代码块。但是这种同步并不能起到线程同步的作用。跟第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。

6、双重检查[推荐用]

public class Singleton {

private static volatile Singleton singleton;

private Singleton() {}

public static Singleton getInstance() {

if (singleton == null) {

synchronized (Singleton.class) {

if (singleton == null) {

singleton = new Singleton();

}

}

}

return singleton;

}

}

Double-Check概念对于多线程开发者来说不会陌生,如代码中所示,我们进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。

优点:线程安全;延迟加载;效率较高

7、静态内部类[推荐用]

public class Singleton {

private Singleton() {}

private static class SingletonInstance {

private static final Singleton INSTANCE = new Singleton();

}

public static Singleton getInstance() {

return SingletonInstance.INSTANCE;

}

}

这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。

类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。优点:避免了线程不安全,延迟加载,效率高。

8、枚举[推荐用]

public enum Singleton {

INSTANCE;

public void whateverMethod() {

}

}

借助JDK1.5中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。可能是因为枚举在JDK1.5中才添加,所以在实际项目开发中,很少见人这么写过。

优点

系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。

缺点

当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。

适用场合

需要频繁的进行创建和销毁的对象;创建对象时耗时过多或耗费资源过多,但又经常用到的对象;工具类对象;

频繁访问数据库或文件的对象。

 

假如你现在还在为自己的技术担忧,假如你现在想提升自己的工资,假如你想在职场上获得更多的话语权,假如你想顺利的度过35岁这个魔咒,假如你想体验BAT的工作环境,那么现在请我们一起开启提升技术之旅吧,详情请点击

 

kk

转载地址:http://bfkjm.baihongyu.com/

你可能感兴趣的文章
redis的导入导出需要特别注意的地方
查看>>
理解思科IPS系统的virtual sensor
查看>>
疯狂ios讲义之创建cocos2d项目(3)
查看>>
《信息系统项目管理师软考辅导——3年真题详解与全真模拟》主要创新点、关注点...
查看>>
当网络安全遇上大数据分析(6)
查看>>
Django学习系列之用户注册
查看>>
丢失日志文件的风险与对策
查看>>
HDU 2152 Fruit
查看>>
Sliverlight调用Rest服务的一点思考和实践
查看>>
提取mkv文件中的字幕
查看>>
C#多线程学习(六) 互斥对象
查看>>
C#和C++混合编程[转]
查看>>
ubuntu 10.10下安装与配置CUDA 4.0
查看>>
Cython: C-Extensions for Python
查看>>
POJ 1423 Big Number
查看>>
How Tomcat Works 学习-我们到底能走多远系列(8)
查看>>
一个父亲给儿子的信
查看>>
SQL SERVER--指定查询优化参数
查看>>
JWhoisServer
查看>>
post方式发微博
查看>>