单例模式的七种实现-Singleton(Java实现)
1. 饿汉式
实现代码:
public class Singleton {
private Singleton() {
}
private static Singleton singleton = new Singleton();
public static Singleton getInstance() {
return singleton;
}
}
验证一下:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
// true
}
如果用反射, 是否仍然是单例:
结果是反射破坏了单例
public static void main(String[] args) throws Exception {
// 自定义单例方法获取
Singleton s1 = Singleton.getInstance();
// 反射获取
Constructor constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s2 = (Singleton) constructor.newInstance();
System.out.println(s1 == s2);
//false
}
2. 懒汉式
将上面的饿汉式改为懒汉式:
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
验证一下:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
// true
}
不过这是一种线程不安全的单例实现.
我们在Singleton中加上sleep来模拟一下线程切换:
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
try {
Thread.sleep(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton = new Singleton();
}
return singleton;
}
}
验证一下线程不安全:
public class Main3 {
private static LinkedBlockingQueue<Singleton> singletons = new LinkedBlockingQueue<>();
public static void main(String[] args) throws Exception{
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for(int i= 0;i<100;i++){
threadPool.execute(()->{
singletons.offer(Singleton.getInstance());
});
}
Singleton basic = singletons.take();
while(basic==singletons.take()){
System.out.println("continue");
continue;
}
System.out.println("走到这里说明单例失败");
}
}

3. 懒汉式+同步方法
public class Singleton {
private Singleton() {
}
private static Singleton singleton;
public synchronized static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
验证一下:
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2);
// true
}
由于是同步, 所以同步方法内不会出现多线程执行的情况.
4. 懒汉式+双重校验锁
因为上面那种会每次进时都会进行同步锁, 很浪费性能, 所以在加锁之间先进行校验
public class Singleton{
private Singleton() {
}
private static Singleton singleton;
public static Singleton getInstance() {
if (singleton==null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
验证一下性能:
能明显看出来性能差距...5千倍...
同步方法, 即直接在方法声明处加了Synchronize的情况:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

双重校验锁:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

5. 懒汉式+双重校验锁+防止指令重拍
看似简单的一段赋值语句:instance = new Singleton(); 其实JVM内部已经转换为多条指令:
memory = allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance = memory; //3:设置instance指向刚分配的内存地址
但是经过重排序后如下:
memory = allocate(); //1:分配对象的内存空间
instance = memory; //3:设置instance指向刚分配的内存地址,此时对象还没被初始化
ctorInstance(memory); //2:初始化对象
可以看到指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面,在线程A初始化完成这段内存之前,线程B虽然进不去同步代码块,但是在同步代码块之前的判断就会发现instance不为空,此时线程B获得instance对象进行使用就可能发生错误。
加上volatile关键字:
public class Singleton {
private Singleton() {
}
private volatile static Singleton singleton;
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
验证一下性能:
能明显看出来加了volatile后对性能的影响, 由之前的5, 变为了302...
性能下降了, 但是相比于上面的双重校验锁, 更保证了线程安全.
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

6. 静态内部类
这也是一种很好的实现方式, 不仅懒加载, 还保证了线程安全, 性能也很好, 实现起来也很简单
public class Singleton {
private static class LazyHolder {
private static final Singleton instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return LazyHolder.instance;
}
}
验证一下性能:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

7. 枚举
个人对枚举类型的理解还有限, 有待学习....
public enum Singleton {
INSTANCE;
private String name;
Singleton() {
this.name = "king";
}
public static Singleton getInstance() {
return INSTANCE;
}
public String getName() {
return this.name;
}
}
验证一下性能:
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i= 0;i<999999999;i++){
Singleton s = Singleton.getInstance();
}
System.out.println(System.currentTimeMillis()-start);
}

单例模式的七种实现-Singleton(Java实现)的更多相关文章
- Java设计模式之单例模式(七种写法)
Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...
- Java 单例模式的七种写法
Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...
- Android设计模式之单例模式的七种写法
一 单例模式介绍及它的使用场景 单例模式是应用最广的模式,也是我最先知道的一种设计模式.在深入了解单例模式之前.每当遇到如:getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式 ...
- Java:单例模式的七种写法
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton ( ...
- Java:单例模式的七种写法(转载)
第一种(懒汉,线程不安全): package Singleton; /** * @echo 2013-10-10 懒汉 线程不安全 */ public class Singleton1 { priva ...
- Java:单例模式的七种写法[转]
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 privat ...
- 【JAVA学习】单例模式的七种写法
尊重版权:http://cantellow.iteye.com/blog/838473 第一种(懒汉.线程不安全): Java代码 public class Singleton { private ...
- Java:单例模式的七种写法<转>
第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 privat ...
- 温故而知新(java实现)单例模式的七种写法
第一种(懒汉,线程不安全): Java代码 public class Singleton { private static Singleton instance; private Singleton ...
随机推荐
- 进程命令(tasklist)
TaskList命令: // 描述: 显示本地或远程计算机上正在运行的进程列表信息. // 语法: tasklist [/s <computer> [ /u [<domain> ...
- LeetCode算法题-Search in a Binary Search Tree(Java实现)
这是悦乐书的第295次更新,第314篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第163题(顺位题号是700).给定一个二叉搜索树(BST)的和正整数val. 你需要在 ...
- 英语口语练习系列-C11-了解
词汇 actor [ˈæktə(r)] n. 男演员 He is a good actor. 他是一个好演员. afternoon [ˌɑ:ftəˈnu:n] n. 下午 a boring after ...
- kaptcha验证码的使用
使用kaptcha可以方便的配置: 验证码的字体 验证码字体的大小 验证码字体的字体颜色 验证码内容的范围(数字,字母,中文汉字!) 验证码图片的大小,边框,边框粗细,边框颜色 验证码的干扰线(可以自 ...
- Django--session(登录用)
一.session的原理图 二.Django中session对象的设置/读取/删除及其他方法 三. Django--配置 settings.py中与session有关的参数 一.session的原理图 ...
- C#基础知识之静态和非静态
项目中静态和非静态常被用到,什么时候需要用静态的,什么时候需要使用非静态,以及他们的区别是什么? 一.概述 静态和非静态的不同地方,就是静态从程序一启动就会一直占用内存,而非静态只有在实例化的时候才会 ...
- .NET CORE学习笔记系列(2)——依赖注入[8]: .NET Core DI框架[服务消费]
原文:https://www.cnblogs.com/artech/p/net-core-di-08.html 包含服务注册信息的IServiceCollection对象最终被用来创建作为DI容器的I ...
- maven 出现错误 -source 1.5 中不支持 diamond 运算符
mvn clean package -DskipTests 出现如下错误: -source 1.5 中不支持 diamond 运算符 [ERROR] (请使用 -source 7 或更高版本以启用 d ...
- kafka-rest:A Comprehensive, Open Source REST Proxy for Kafka
Ewen Cheslack-Postava March 25, 2015 时间有点久,但讲的还是很清楚的 As part of Confluent Platform 1.0 released ab ...
- webpack开发环境和生产环境切换原理
在package.json中有如下设置: "scripts": { "dev": "node build/dev-server.js" ...