单例模式
- !将其中的EnumSingleton 换成对应的类名即可。
- 1、饿汉式 静态常量直接初始化(不推荐)
- 2、饿汉式 类加载静态代码块实现(不推荐)
- 3、懒汉式(多线程不安全)(不推荐)
- 4、懒汉式,在静态方法上加锁 线程安全 耗时长(不推荐)
- 5、懒汉式,DCL 双重校验实现(推荐!)
- 6、静态内部类实现(推荐!!)
- 7、枚举实现(极力推荐!!!)
- 小结:枚举、静态内部类、静态代码块实现原理都是借助了ClassLoader实现的。类加载器在加载类的时候会调用loadClass()方法,JVM在底层实现上已经加了Synchronized同步,详情可见[为什么推荐枚举实现单例](https://blog.youkuaiyun.com/moakun/article/details/80688851)。
- 实现原则:
大家好! 写这篇博客的目的是分析一下单例模式的使用,做到知其然知其所以然。
The real target is that I was asked the 单例模式 usage in the interview. And I cann’t answer it.
真正目的是因为在面试中被问到单例模式在充血模型和贫血模型这块有什么区别。
【一】What is the Singleton Pattern 什么是单例模式
Ensure a class has only one instance, and provide a global point of
access to it.(确保某一个类 只有一个实例,而且自行实例化并向整个系统提供这个实例。)
类图:
【二】How is the Singleton Pattern to realize 单例如何实现
七种写法:
测试方法代码:
public class ThreadPoolTest {
private static volatile long startTime;
private static volatile long endTime;
static Executor flxedExecutor = new ThreadPoolExecutor(10,10,0, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
public void submit(){
flxedExecutor.execute(new Runnable() {
@Override
public void run() {
EnumSingleton singleton = EnumSingleton.INSTANCE;
System.out.println(singleton.toString());
}
});
}
public static void main(String[] args) {
ThreadPoolTest test = new ThreadPoolTest();
startTime = System.nanoTime();
System.out.println("开始时间:" + startTime+"ns");
for (int i = 0; i < 1000; i++) {
test.submit();
}
endTime = System.nanoTime();
System.out.println("结束时间:" + endTime+"ns");
System.out.println("耗时:" + (endTime-startTime)+"ns");
}
}
!将其中的EnumSingleton 换成对应的类名即可。
1、饿汉式 静态常量直接初始化(不推荐)
public class BasicSingleton {
private static BasicSingleton singleton = new BasicSingleton();
/* 提供外部访问方法 */
public static BasicSingleton getInstance(){
return singleton;
}
/* 构造函数私有化 */
private BasicSingleton(){
}
}
- 优点:类加载的时候完成了初始化。有效避免线程同步问题
- 缺点:类加载完成初始化,未达到懒加载。若此实例从未使用过,则造成内存浪费
测试结果:
2、饿汉式 类加载静态代码块实现(不推荐)
public class StaticBlockSingleton {
static {
singleton = new StaticBlockSingleton();
}
private static StaticBlockSingleton singleton;
public static StaticBlockSingleton getInstance(){
return singleton;
}
private StaticBlockSingleton(){
}
}
- 优缺点同第一种
测试结果:
3、懒汉式(多线程不安全)(不推荐)
public class LazySingleton {
private static LazySingleton singleton;
private LazySingleton(){
}
public static LazySingleton getInstance(){
if(singleton == null){
singleton = new LazySingleton();
}
return singleton;
}
}
- 优点:起到Lazy Loading延迟加载效果
- 缺点:多线程不安全
测试结果:不安全
耗时:
4、懒汉式,在静态方法上加锁 线程安全 耗时长(不推荐)
public class SyncLazySingleton {
private static SyncLazySingleton singleton;
private SyncLazySingleton(){
}
public static synchronized SyncLazySingleton getInstance(){
if(singleton == null){
singleton = new SyncLazySingleton();
}
return singleton;
}
}
- 优点:线程安全
- 缺点:静态方法加锁 相当于类锁 只有第一次时需加锁。 以后再来的时候 直接return即可
测试结果:
5、懒汉式,DCL 双重校验实现(推荐!)
public class DCLSingleton {
private static DCLSingleton singleton;
private DCLSingleton(){
}
public static synchronized DCLSingleton getInstance(){
if(singleton == null){
synchronized (DCLSingleton.class){
if(singleton == null){
singleton = new DCLSingleton();
}
}
}
return singleton;
}
}
- 优点:线程安全、延迟加载、效率高
耗时:
- 缺点:序列化时导致的单例不安全
6、静态内部类实现(推荐!!)
public class StaticInnerClassSingleton {
private StaticInnerClassSingleton(){}
private static class InnerClass{
private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance(){
return InnerClass.INSTANCE;
}
}
- 当加载外部类的时候,并不会去加载这个实例,只有当用到实例INSTANCE时才会去加载
7、枚举实现(极力推荐!!!)
public enum EnumSingleton {
INSTANCE;
}
小结:枚举、静态内部类、静态代码块实现原理都是借助了ClassLoader实现的。类加载器在加载类的时候会调用loadClass()方法,JVM在底层实现上已经加了Synchronized同步,详情可见为什么推荐枚举实现单例。
【三】Why use the Singleton Pattern 为什么使用单例
- 节省内存 因为只有一个对象产生
- 减少系统的性能开销
- 对于需要频繁创建和销毁的对象来说可以提高系统的性能。
实现原则:
实现单利模式的原则和过程:
1.单例模式:确保一个类只有一个实例,自行实例化并向系统提供这个实例
2.单例模式分类:饿单例模式(类加载时实例化一个对象给自己的引用),懒单例模式(调用取得实例的方法如getInstance 时才会实例化对象)(java中饿单例模式性能优于懒单例模式,c++中一般使用懒单例模式)
3.单例模式要素:
- 私有构造方法
- 私有静态引用指向自己实例
- 以自己实例为返回值的公有静态方法
应用场景:
应用场景举例:
1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
参考目录:
https://blog.youkuaiyun.com/liu_ser/article/details/80328999
《设计模式之禅》
《大话设计模式》
类加载参考:《深入理解JVM》周志明大大的