概念
单例:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式是一种常用的软件设计模式之一,其目的是保证整个应用中只存在类的唯一个实例。
比如我们在系统启动时,需要加载一些公共的配置信息,对整个应用程序的整个生命周期中都可见且唯一,这时需要设计成单例模式。如:spring容器,session工厂,缓存,数据库连接池等等。
如图
特点
1)单例模式只能有一个实例。
2)单例类必须创建自己的唯一实例。
3)单例类必须向其他对象提供这一实例。
如何保证实例的唯一
1)保证实例化一次
2)由类本身进行实例化
3)防止外部初始化
4)对外提供获取实例的方法
5)线程安全
几种单例模式以及优缺点
懒汉模式
在用的时候才加载,延迟加载
线程不安全示例
public class SlackerSingletonDemo {
private static SlackerSingletonDemo instance;
private SlackerSingletonDemo(){
}
public static SlackerSingletonDemo getInstance() {
if (instance == null) {
instance = new SlackerSingletonDemo();
}
return instance;
}
}
优点
在获取实例的方法中,进行实例的初始化,节省系统资源
缺点
1.如果获取实例时,初始化工作较多,加载速度会变慢,影响系统系能
2.每次获取实例都要进行非空检查,系统开销大
3.非线程安全,当多个线程同时访问getInstance()时,可能会产生多个实例
如果需要线程安全 在getInstance 加synchronized
线程安全示例
public class SlackerSingletonDemo {
private static SlackerSingletonDemo instance;
private SlackerSingletonDemo(){
}
public static synchronized SlackerSingletonDemo getInstance() {
if (instance == null) {
instance = new SlackerSingletonDemo();
}
return instance;
}
}
这种的缺点就是synchronized锁占用的资源浪费
饿汉模式
在定义类的静态私有变量同时进行实例化
public class HungrySingletonDemo {
private static HungrySingletonDemo instance = new HungrySingletonDemo();
private HungrySingletonDemo(){
}
public static HungrySingletonDemo getInstance() {
return instance;
}
}
1.声明静态私有类变量,且立即实例化,保证实例化一次
2.私有构造,防止外部实例化(通过反射是可以实例化的,不考虑此种情况)
3提供public的getInstance()方法供外部获取单例实例
好处
1.线程安全
2.获取实例速度快
缺点
类加载即初始化实例,内存浪费
DCL模式
Double Check Lock,双重锁判断机制
public class DCLSingletonDemo {
private volatile static DCLSingletonDemo instance;
private DCLSingletonDemo(){
}
public static DCLSingletonDemo getInstance() {
if (instance == null) {
synchronized (DCLSingletonDemo.class){
if (instance == null) {
instance = new DCLSingletonDemo();
}
}
}
return instance;
}
}
优点
线程安全,进行双重检查,保证只在实例未初始化前进行同步,效率高
缺点
还是实例非空判断,耗费一定资源
静态内部类实现模式
线程安全,调用效率高,可以延时加载
public class StaticSingletonDemo {
private static class StaticSingletonDemoInstance{
private static final StaticSingletonDemo instance=new StaticSingletonDemo();
}
private StaticSingletonDemo(){
}
public static StaticSingletonDemo getInstance(){
return StaticSingletonDemoInstance.instance;
}
}
优点:既避免了同步带来的性能损耗,又能够延迟加载
枚举模式
public enum EnumSingletonDemo {
//枚举元素本身就是单例
INSTANCE;
public void init() {
System.out.println("资源初始化。。。");
}
}
优点:天然线程安全,可防止反射生成实例。
如何选用
-单例对象 占用资源少,不需要延时加载,枚举 好于 饿汉
-单例对象 占用资源多,需要延时加载,静态内部类 好于 懒汉
单例模式的优缺点
优点
1.该类只存在一个实例,节省系统资源
2.对于需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点
不能外部实例化(new),调用人员不清楚调用哪个方法获取实例时会感到迷惑,尤其当看不到源代码时。
推荐阅读:
-
进程同步的五种机制以及优缺点(翻译)
-
redis五种数据类型的实现方式,常用命令,应用场景
-
redis和memcahed的共同点,区别以及应用场景
-
详解TCP的三次握手与四次挥手及面试题(很全面)
-
基础面试题:Arrays 工具类详解(超详细)
-
算法必须掌握几种方法
-
QPS、TPS、并发用户数、吞吐量