单例模式是最常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生。
因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此有些设计大师并把把其称为设计模式之一。
简介
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
应用场景
1. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
2. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。所有桌面上面的东西,比如视频,文件等等,删除之后都会进入到它里面.专业术语讲:就是始终是一个对象实例.
3、火车站买票,多个窗口同时去打印车票,但是车票数量就是这么多,防止出现超售。当需要对同一个资源进行利用时,就好比火车卖票,就那么一堆资源,需要多个线程进行处理,而单例就可以解决了,
4、一个播放器程序,当用户打开一个播放音乐界面,再想打开另一个音乐播放时,之前的界面就关闭了。这就是一个单利模式的具体应用
5、java Runtime 类:Runtime类本身就是单例设计模式的一种应用,因为整个JVM中只存在一个Runtime类的对象,可以使用Runtime类取得JVM的系统信息,或者使用gc()方法释放掉垃圾空间,还可以运行本机的程序。
Runtime 代表着Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,我们可以通过Runtime.getRuntime() 方法来获取当前程序的Runtime实例。
基本的实现思路
单例模式要求类能够有返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称)。
单例的实现主要是通过以下两个步骤:
将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
饿汉式&懒汉式
单例中懒汉和饿汉的区别在于以下几点:
1、饿汉式是线程安全的,在类创建的同时就已经创建好一个静态的对象供系统使用,以后不在改变。懒汉式如果在创建实例对象时不加上synchronized则会导致对对象的访问不是线程安全的。
2、从实现方式来讲他们最大的区别就是懒汉式是延时加载,他是在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建,饿汉式无需关注多线程问题、写法简单明了、能用则用
懒汉和饿汉的本质区别,就是实例化对象的时机,即是什么时候将对象创建起来
懒汉模式:
1、饿汉式(静态常量)
可以防止多线程访问生成两个不一样的实例。
上面已经实例化了,但是一直没有被调用,会导致内存浪费
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return INSTANCE;
}
}
2、饿汉式(静态代码块)
public class Singleton {
private static volatile Singleton instance;
static {
instance = new Singleton();
}
private Singleton() {}
public Singleton getInstance() {
return instance;
}
}
3、懒汉式(线程不安全)
这种写法起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式。
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static volatile Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
4、懒汉式(线程安全,同步方法)
使用synchronized 实现了线程同步。 但是较耗时。每个线程在想获得类的实例时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行了。
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
5、懒汉式(线程安全,同步代码块)
第3种实现方式遇到的情形一致,假如一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。
public class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
singleton = new Singleton();
}
}
return singleton;
}
}
6、双重检查
进行了两次if (singleton == null)检查,这样就可以保证线程安全了。这样,实例化代码只用执行一次,后面再次访问时,判断if (singleton == null),直接return实例化对象。
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;
}
}
7、静态内部类
延迟加载:采用了类装载的机制来保证初始化实例时只有一个线程。静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
8、枚举单例
系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象
public enum EnumSingleton {
INSTANCE;
private Resourceinstance;
EnumSingleton() {
instance =new Resource();
}
public ResourcegetInstance() {
return instance;
}
}
拓展:
volatile 与 synchronzed
volatile 只能修饰变量。synchronzed还可修饰方法
synchronized不仅保证可见性,而且还保证原子性
volatile 保证可见性,但不能保证原子性。
参考:http://www.cnblogs.com/Mainz/p/3556430.html#
https://www.cnblogs.com/zhaoyan001/p/6365064.html
工厂模式:http://blog.csdn.net/zxt0601/article/details/52798423