设计模式-单例模式
单例模式就是采用一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法)。
JDK中,java.lang.Runtime就是经典的单例模式(饿汉式)。
饿汉式(静态常量)
- 构造器私有化(防止被new)
- 类的内部创建对象
- 向外暴露一个静态的公共方法(getInstance)
public class SingletonTest01 {
// 私有化SingletonTest01使其无法被new
private SingletonTest01() {
}
private final static SingletonTest01 instance = new SingletonTest01();
public static SingletonTest01 getInstance(){
return instance;
}
}
方式可用但可能造成内存浪费
饿汉式(静态代码块)
public class SingletonTest02 {
private SingletonTest02() {
}
private static SingletonTest02 instance;
static {
instance = new SingletonTest02();
}
public static SingletonTest02 getInstance(){
return instance;
}
}
也可能造成内存浪费
懒汉式(线程不安全)
public class SingletonTest03 {
private SingletonTest03() {
}
private static SingletonTest03 instance;
// 用到该方法时 才去创建
public static SingletonTest03 getInstance(){
if(instance == null){
instance = new SingletonTest03();
}
return instance;
}
}
如果在多线程下,多个线程同时进入这个判断语句就会产生多个实例,所以多线程环境下不可使用这种方式
懒汉式(线程安全)
public class SingletonTest04 {
private SingletonTest04() {
}
private static SingletonTest04 instance;
// 用到该方法时 才去创建
public static synchronized SingletonTest04 getInstance(){
if(instance == null){
instance = new SingletonTest04();
}
return instance;
}
}
解决了线程安全问题,但是效率太低了,每个线程想获得实例时,执行getInstance()方法时都要进行同步
实际开发中不推荐
懒汉式(线程安全,同步代码块)
本意是想对第四种同步方法进行优化,但还是可以有多条线程进入if判断语句,所以线程并不安全
不能使用
双重检查(DoubleCheck)
public class SingletonTest05 {
private SingletonTest05() {
}
private static volatile SingletonTest05 instance;
public static synchronized SingletonTest05 getInstance(){
if(instance == null){
synchronized (SingletonTest05.class){
if(instance == null){
instance = new SingletonTest05();
}
}
}
return instance;
}
}
好处就在于进行了两次if(singleton == null)检查,保证线程安全,实例化代码只用执行一次,后面再次访问时外层if判断避免了反复的方法同步
线程安全;延迟加载;效率高;在实际开发中推荐使用这种单例模式
静态内部类
public class SingletonTest06 {
private SingletonTest06() {
}
private static class SingletonInstance{
private static final SingletonTest06 INSTANCE = new SingletonTest06();
}
public static synchronized SingletonTest06 getInstance(){
return SingletonInstance.INSTANCE;
}
}
采用了类装载的机制来保护初始化实例时只有一个线程,静态内部类在类装载的时候不会实例化,而是在需要实例化的时,调用getInstance方法,才会装载SingletonInstance类,类的静态属性指挥在第一次加载时初始化,所以JVM帮我们保证了线程的安全性,类初始化时,别的线程时无法进入的。
避免了线程不安全,利用静态内部类特点实现延迟加载,效率高推荐使用
枚举方式
public enum SingletonTest07 {
INSTANCE;// 属性
public void sayOK(){
System.out.println("ok");
}
}
单例模式注意事项
- 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些频繁创建销毁的对象,使用单例模式可以提高性能
- 当想实例化一个单例类时,必须记住使用相应的获取对象的方法
- 单例模式使用场景:被常用到的需要频繁进行创建和销毁的对象、重量级对象(比如数据源、session工厂等)