单例模式
简介
这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意
- 单例类只能有一个实例;
- 单例类必须自己创建自己的唯一实例;
- 单例类必须给所有其他对象提供这一实例;
作用:某个类在整个系统中只能有一个实例对象可以被获取和使用
优点
- 只生成一个实例,减少系统性能开销;
- 可以在系统设置全局访问点,优化共享资源访问;
缺点
- 没有接口
- 不能继承
- 与单一职责原则冲突
- 一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
使用场景
单例模式保证了系统内存中该类只存在一个对象,节省了系统资源。对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能,(比如工具类对象、频繁访问数据库或文件的对象)
注意:当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new;
synchronized关键字
在Java中,synchronized
锁可能是我们最早接触的锁了
synchronized的作用
- 锁住类
- 锁住对象实例
synchronized的3个用法
- 静态方法加上关键字:那么它获取的就是这个类的锁,锁住的就是这个类
- 实例方法(也就是普通方法)加上关键字:那么它获取的就是这个累的锁,锁住的就是这个对象实例
- 方法中使用同步代码块(性能好)
volatile关键字
volatile
是Java
提供的一种轻量级的同步机制,在并发编程中,它也扮演着比较重要的角色同synchronized
相比(synchronized
通常称为重量级锁),volatile
更轻量级。
可见性
所谓可见性,是指当一条线程修改了共享变量的值,新值对于其他线程来说是可以立即得知的。很显然,上述的例子中是没有办法做到内存可见性的。
实现
方式
- 饿汉式(线程安全,调用效率高,不能延时加载)
- 懒汉式(线程安全,调用效率不高,能延时加载)
- 懒汉式-双重检查锁式(JVM底层内部模型原因,偶尔会出问题)
- 静态内部类式(线程安全,调用效率高,能延时加载)
- 枚举单例(线程安全,调用效率高,不能延时加载)
饿汉式
class Singleton{
//饿汉式,类加载的时候就进行初始化,避免了线程的同步问题
private static final Singleton singleton = new Singleton();
private Singleton() {} //只能在当前类访问它,不能被继承,不能被其他程序用new创建实例对象
public static Singleton getSingleTon(){
return singleton;
}
}
优点:写法简单
缺点:如果从未使用过这个实例,则会造成内存的浪费,也没有达到延迟加载的效果
懒汉式
class Singleton{
private static Singleton singleton;
private Singleton() {} //将构造器 私有化,防止外部调用
//懒汉式,在第一次调用的时候,即getInstance()方法才进行初始化
public synchronized static Singleton getInstance() {
if (singleton ==null) { //如果为空,创建本实例
singleton\=new Singleton();
}
return singleton;
}
}
优点:起到了延迟加载的效果,但只能在单线程下使用
缺点:如果在多线程下,一个线程进入了if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以 在多线程环境下不可使用这种方式;
懒汉式-双重检查锁式
在双重检查锁中,代码会检查两次单例类是否有已存在的实例
一次加锁,一次不加锁,一次确保不会有多个实例被创建
class Singleton{
//为了解决访问的是一个初始化未完成的对象,用volatile关键字修饰
//使用了关键字后,重排序被禁止,所有的写操作都将发生在读操作之前
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (null == singleton) { //先判断对象是否已经被初始化,再决定要不要加锁
synchronized (Singleton.class) {
if (null == singleton) {
singleton =new Singleton();
}
}
}
return singleton;
}
}
优点:在浪费性能的情况下,解决了在多线程的情况下,这样写可能会导致singleton有多个实例
静态内部类式
//懒汉式:静态内部类形式
public class SingleTon {
private SingleTon(){}
private static class Inner{
private static final SingleTon SINGLE_TON = new SingleTon();
}
public static SingleTon getSingleTon(){
return Inner.SINGLE_TON;
}
}
优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高
缺点:在类进行初始化时,别的线程是无法进入的
枚举单例
借助JDK1.5
中添加的枚举来实现单例模式
/*
* 枚举类型:表示该类型的对象是有限的几个
* 我们可以限定为一个,就成了单例
*/
public enum SingleTon {
INSTANCE;
}
优点:不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象