一、设计模式概述和分类
1.创建型模式
单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式
2.结构型模式
适配者模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
3.行为型模式
模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式
单例模式
就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
一、饿汉式(静态常量)
- 构造器私有化(防止new)
- 类的内部创建对象
- 向外暴露一个静态的公共方法getInstance()
public class Singleton1 {
//1.构造器私有化
private Singleton1(){
}
//2.内部创建对象实例
private final static Singleton1 instance = new Singleton1();
//3.提供一个公有的静态方法,返回实例对象
public static Singleton1 getInstance(){
return instance;
}
}
问题:类装载即完成实例化,可能造成内存浪费。
二、饿汉式(静态代码块)
实例的创建放在静态代码块中
public class Singleton2 {
private Singleton2(){
}
private static Singleton2 instance;
//静态代码块中创建单例对象
static {
instance = new Singleton2();
}
public static Singleton2 getInstance(){
return instance;
}
}
和上述方式相同
三、懒汉式(线程不安全)
不可用
public class Singleton3 {
private static Singleton3 instance;
private Singleton3(){
}
//当使用该方法时,才会创建instance
public static Singleton3 getInstance(){
if(instance==null){
instance = new Singleton3();
}
return instance;
}
}
使用到getInstance()方法时,才会生成实例对象。
但是,这是线程不安全的。一旦多个线程同时到达if(instance==null)
,并判断为空,那么多个线程都会执行new Singleton3()
,会创建多个实例。
四、懒汉式(线程安全)
public class Singleton3 {
private static Singleton3 instance;
private Singleton3(){
}
//当使用该方法时,才会创建instance
public static synchronized Singleton3 getInstance(){
if(instance==null){
instance = new Singleton3();
}
return instance;
}
}
在方法上添加synchronized关键字,就可以解决线程安全问题,但是效率很低。因为整个方法都是同步的,所有的线程必须串行执行。
五、懒汉式(同步代码块)
不可用
public class Singleton3 {
private static Singleton3 instance;
private Singleton3() {
}
//当使用该方法时,才会创建instance
public static Singleton3 getInstance() {
if (instance == null) {
synchronized (Singleton3.class) {
instance = new Singleton3();
}
}
return instance;
}
}
将同步的机制放在if (instance == null)
代码块中,但是只要进入了if代码块,还是会出现线程安全问题,可以说和不加没什么区别。
六、双重检查
使用volatile修饰实例,让instance对所有线程是可见的。
同时,在上一个方法的基础上,进入到synchronized代码块中之后,再次进行判断if (instance == null)
。
外面的if
保证效率,里面的if
保证安全。
public class Singleton3 {
private static volatile Singleton3 instance;
private Singleton3() {
}
//当使用该方法时,才会创建instance
public static Singleton3 getInstance() {
if (instance == null) {
synchronized (Singleton3.class) {
if (instance == null) {
instance = new Singleton3();
}
}
}
return instance;
}
}
既保证了线程安全,又保证了懒加载。
七、静态内部类
静态内部类的特点:
- 当外部类被装载时,静态内部类不会被装载。
- 只会被装载一次
public class Singleton5 {
private Singleton5() {
}
private static class SingletonInstance{
private static final Singleton5 INSTANCE = new Singleton5();
}
public static Singleton5 getInstance(){
return SingletonInstance.INSTANCE;
}
}
- 这种方式采用了类装载的机制来保证初始化实例时只有一个线程。
- 静态内部类方式在Singleton类被装载的时候并不会立即实例化,而是在需要实例化的时候,调用getInstance()方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
- 类的静态属性只会在第一次加载类的时候初始化,所以JVM帮我们保证了线程的安全性。
八、枚举
public enum Singleton4 {
instance;
}
使用枚举来实现,不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
单例模式在JDK源码中的使用
java.lang.Runtime就是最经典的单例模式,用的是饿汉式。
小结
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
- 当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new;
- 使用场景:需要频繁的进行创建和销毁的对象、创建对象耗时过多或耗费资源过多,但又经常使用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)