Java中的单例模式(Singleton Pattern in Java)
Introduction
单例模式在很多的框架中被广泛使用。
对于系统中的某个类来说,只有一个实例是很重要的,比如只能有一个timer和ID Producer。又比如在服务器程序中,配置信息保留在一个文件中,这些配置信息只由一个单例对象统一获取,进程中的其他对象通过这个单例对象获取这些配置信息,通过这种方式大大简化复杂环境下的配置管理。
这个时候一个类里面就只能有一个实例,并且这个实例要易于访问。我当然可以只定义一个全局变量从而保证对象随时都能访问。但是如果是这种方式来实现单例模式的话,依然可以实例化多个instance,而且被不同的对象所持有(这就违反了单例模式的条件了)不是很妙。
实现单例模式的思路是:
一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
如果类自身负责保存它的唯一实例。这个类可以保证没有其他实例被创建,并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。
Definition
Singleton Pattern:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
- 单例类只能由一个实例
- 自行创建实例
- 自行向整个系统(所有其他对象)提供此实例
Attention
在多线程的应用场合中使用单例模式的时候要注意。如果唯一的单例尚未被创建(懒汉模式),有两个线程同时调用构建的方法,他俩都不会检测到唯一实例,于是每个线程都创建了一个实例,这就违反了单例模式中实例唯一的原则。
所以我们可以为指示类是否实例化的变量提供一个互斥锁。(会降低效率)
Java实现
常用的构建方式
- 懒汉模式。指全局的单例模式在第一次使用时被创建。
- 饿汉方式。指全局的单例模式在类装载的时候被创建。
Example
- 饿汉模式
public class Sington{
private static Singleton instance = new Singleton();
private Singleton (){
}
public static Singleton getInstance() {
return instance;
}
}
在类加载的时候完成初始化,所以类的加载速度会比较慢,但是可以很快的获取对象,优势在于不需要去考虑多线程的同步问题。
懒汉模式
2.1 lock
public class Singleton {
private static Singleton instance; private Singleton() {
} public synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();
}
return instance;
}
}
这样来加锁是一个比较简单粗暴的方式,但是比较无脑synchronized,每次调用getInstance都会进行一次同步,会有一定的性能消耗,实际上我只需要第一次初始化的时候加锁就好了。
2.2 double checked locking
public class Singleton {
private static volatile Singleton instance = null;
// Private constructor suppresses
// default public constructor
private Singleton() {};
//Thread safe and performance promote
public static Singleton getInstance() {
if(instance == null){
synchronized(Singleton.class){
// When more than two threads run into the first null check same time,
// to avoid instanced more than one time, it needs to be checked again.
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查锁的优势在于它先判断了对象是否已经初始化,再决定要不要加锁。
使用了volatile关键字之后,所有的写操作发生在读操作之前。这个解决方案需要 JDK5 或更高版本(因为从 JDK5 开始使用新的 JSR-133 内存模型规范,这个规范增强了 volatile 的语义)
- Initialization On Demand Holder idiom
JVM 在类的初始化阶段(即在 Class 被加载后,且被线程使用之前),会执行类的初始化。在执行类的初始化期间,JVM 会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化。
public class Singleton {
public class InstanceFactory {
private static class InstanceHolder {
public static Instance instance = new Instance();
}
public static Instance getInstance() {
return InstanceHolder.instance ; // 这里将导致 InstanceHolder 类被初始化 (只有第一次调用getInstance方法的时候,虚拟机加载InstanceHolder并且初始化instance)
}
}
}
不是很清楚这种基于类初始化的方案和上面的双重检查模式到底谁的性能更好。
但基于 volatile 的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。
参考
Java中的单例模式(Singleton Pattern in Java)的更多相关文章
- Java 设计模式(三)-单例模式(Singleton Pattern)
1 概念定义 1.1 定义 确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 1.2 类型 创建类模式 1.3 难点 1)多个虚拟机 当系统中的单例类被拷贝运行在多 ...
- Java 基础:单例模式 Singleton Pattern
1.简介 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创 ...
- 设计模式之单例模式(Singleton Pattern)
单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...
- Java设计模式之单例模式 - Singleton
用来创建独一无二的,是能有一个实例的对象的入场券.告诉你一个好消息,单例模式的类图可以说是所有模式的类图中最简单的,事实上,它的类图上只有一个类!但是,可不要兴奋过头,尽管从类设计的视角来说很简单,但 ...
- java中的单例模式与静态类
单例模式与静态类(一个类,所有方法为静态方法)是另一个非常有趣的问题,在<Java中有关单例模式的面试问题>博文中露掉了,由于单例模式和静态类都具有良好的访问性,它们之间有许多相似之处,例 ...
- 浅谈设计模式--单例模式(Singleton Pattern)
题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...
- 抽象工厂(Abstract Factory),工厂方法(Factory Method),单例模式(Singleton Pattern)
在谈工厂之前,先阐述一个观点:那就是在实际程序设计中,为了设计灵活的多态代码,代码中尽量不使用new去实例化一个对象,那么不使用new去实例化对象,剩下可用的方法就可以选择使用工厂方法,原型复制等去实 ...
- 设计模式系列之单例模式(Singleton Pattern)——确保对象的唯一性
模式概述 模式定义 模式结构图 饿汉式单例与懒汉式单例 饿汉式单例 懒汉式单例 模式应用 模式在JDK中的应用 模式在开源项目中的应用 模式总结 主要优点 适用场景 说明:设计模式系列文章是读刘伟所著 ...
- 【设计模式】单例模式 Singleton Pattern
通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance) 的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...
- java中myeclipse连接mysql问题(java.lang.ClassNotFoundException: com.mysql.jdbc.Driver)
java中myeclipse连接mysql问题(java.lang.ClassNotFoundException: com.mysql.jdbc.Driver) 1.往项目中添加mysql-conne ...
随机推荐
- 关于XML异步
记得有次面试的时候面试官问我知道AJAX吗?当时我回答听过但是没去看过,当时只是知道它和异步的概念有关. 经过查资料,弄明白了些头绪,下面就把我自己对AJAX的理解说说. 大多数浏览器是支持XMLHt ...
- 声谱预测网络(Tacotron2)
整个特征预测网络是一个带有注意力机制(attention)的seq2seq网络. 编码器-解码器(Encoder-Decoder)结构 在原始的编码器-解码器结构中,编码器(encoder)输入一个序 ...
- 使用 docker 搭建 nginx+php-fpm 环境 (两个独立镜像)
:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdow ...
- redis连接错误3种解决方案System Error MISCONF Redis is configured to save RDB snapshots
redis连接错误System Error MISCONF Redis is configured to save RDB snapshots, but XX 情况1解决办法: 由于强制停止red ...
- C# 设计模式,工厂方法
C#工厂方法 using System; using System.Collections.Generic; using System.Linq; using System.Text; using S ...
- 原子操作CAS-最小的线程安全
原文连接:(http://www.studyshare.cn/blog-front/blog/details/1166/0 )一.原子操作是什么? 如果有两个线程分别执行两个操作A和B,从第一个线程执 ...
- vuex分模块3
nuxt 踩坑之 -- Vuex状态树的模块方式使用 原创 2017年12月20日 11:24:14 标签: vue / nuxt / vuex / 模块化 / 状态管理 874 初次看到这个模块方式 ...
- 【过时】update progress has encountered a problem解决办法
笔者第二次整理博客,已经抛弃MyEclipse了,我将公司项目转换成了idea的目录结构后大家都改换Idea进行开发,虽然我个人比较喜欢eclipse的简洁干净,但是Idea的确有很多方便开发的新功能 ...
- 【记录】mybatis中获取常量类中数据
部分转载,已注明来源: 1.mybatis中获取常量类中数据 <update id="refuseDebt"> UPDATE dt_debt a SET ...
- Codeforces Round #564 (Div. 2)B
B. Nauuo and Chess 题目链接:http://codeforces.com/contest/1173/problem/B 题目 Nauuo is a girl who loves pl ...