设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这是前辈们经过相当长的一段时间的试验总结出来的,谈起java的23种设计模式,仍记得初次接触时的迷惑...本次就来回顾总结设计模式中最简单的单例模式。
1.什么是单例模式
是一种常用的软件设计模式,单例模式保证系统中,一个类有且仅有一个实例,并且提供一个访问该实例的全局访问点。
特点:
类构造器私有
持有自己类型的属性
对外提供获取实例的静态方法
单例模式的实现有很多种
1. 饿汉式
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
饿汉式:线程安全,但类加载的时候就创建本类的对象,容易产生垃圾,消耗内存。
2.懒汉式
public class Singleton {
//定义一个变量来储存创建好的类实例
private static Singleton instance;
//构造方法私有化
private Singleton (){
}
//定义一个方法供外界访问该类的实例
public static Singleton getInstance() {
//判断储存实例的变量是否有值
if (instance == null) {
//如果没有,就创建一个类实例,并赋值给储存类实例的变量
instance = new Singleton();
}
//如果有值就直接使用
return instance;
}
}
懒汉模式:线程不安全,但延迟初始化。在多线程中,此方法不能保证单例的状态。
3. 双重锁模式(JDK1.5起)
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) { //避免不必要的实例
synchronized (Singleton.class) {
if (singleton == null) { //进行同步,避免多线程的问题
singleton = new Singleton();
}
}
}
return singleton;
}
}
双重锁模式:线程安全,延迟初始化,采用双重锁机制,安全且在多线程情况下能保持高性能。(将静态实例属性加上关键字volatile,标识这个属性是不需要优化的。大概就是禁止了JVM自动的指令重排优化,)
4. 静态内部类
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return Inner.instance;
}
private static class Inner {
private static final Singleton instance = new Singleton();
}
}
静态内部类单例模式:只有第一次getInstance方法时,虚拟机才去加载INner并初始化instance,只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,保证对象的唯一性。目前此方法是所有单例模式中最推荐的模式,但具体还是根据项目选择。
5. 枚举(JDK1.5起)
默认枚举实例的创建是线程安全的,并且在任何情况下都是单例。其调用效率高,不能延迟加载。实际上
- 枚举类隐藏了私有的构造器。
- 枚举类的域 是相应类型的一个实例对象
public enum Singleton {
INSTANCE
//doSomething 该实例支持的行为
//可以省略此方法,通过Singleton.INSTANCE进行操作
public static Singleton get Instance() {
return Singleton.INSTANCE;
}
}
延迟加载思想
什么是延迟加载呢?
通俗点说,就是一开始不要加载资源或者数据,一直等到马上就要使用这个资源或者数据了才加载,所以也称Lazy Load,不是懒惰,是“延迟加载”,这在实际开发中是一种很常见的思想,尽可能的节约资源。(懒汉式)
缓存思想
简单讲就是,如果某些资源或者数据会被频繁的使用,可以把这些数据缓存到内存里面。每次操作的时候,先到内存里面找,看有没有这些数据,如果有,那么就直接使用,如果没有那么就获取它,并设置到缓存中,下一次访问的时候可以直接从内存中获取了。从而节省大量的时间,当然,缓存是一种典型的空间换时间的方案。
单例模式的懒汉式实现还体现了缓存的思想,缓存也是实际开发中非常常见的功能。
单例模式的优缺点
优点:
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
(比如Servlet编程中,每个Servlet是单例的。Spring中的每个bean默认是单例)
2、避免对资源的多重占用(比如写文件操作)。
缺点:
没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
单例模式线程安全吗?
单例也不能保证100%线程安全的。解决方法就是创建实例方法中加入Java关键字synchronized。
Java语言的关键字synchronized,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍然可以访问该object中的非加锁代码块。
在实际的应用场景中,具体情况具体分析。。。