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实现)的更多相关文章

  1. Java设计模式之单例模式(七种写法)

    Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...

  2. Java 单例模式的七种写法

    Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...

  3. Android设计模式之单例模式的七种写法

    一 单例模式介绍及它的使用场景 单例模式是应用最广的模式,也是我最先知道的一种设计模式.在深入了解单例模式之前.每当遇到如:getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式 ...

  4. Java:单例模式的七种写法

    第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton ( ...

  5. Java:单例模式的七种写法(转载)

    第一种(懒汉,线程不安全): package Singleton; /** * @echo 2013-10-10 懒汉 线程不安全 */ public class Singleton1 { priva ...

  6. Java:单例模式的七种写法[转]

    第一种(懒汉,线程不安全):  1 public class Singleton {   2     private static Singleton instance;   3     privat ...

  7. 【JAVA学习】单例模式的七种写法

    尊重版权:http://cantellow.iteye.com/blog/838473 第一种(懒汉.线程不安全): Java代码   public class Singleton { private ...

  8. Java:单例模式的七种写法<转>

    第一种(懒汉,线程不安全):  1 public class Singleton {   2     private static Singleton instance;   3     privat ...

  9. 温故而知新(java实现)单例模式的七种写法

    第一种(懒汉,线程不安全): Java代码 public class Singleton { private static Singleton instance; private Singleton ...

随机推荐

  1. #021 Java复习第一天

    上学期在慧河工作室学习简单过java到面向对象就停止了 现在有事情又要用到java发现全忘了..... 快速复习一下 网课PPT 计算机: 硬件 + 软件 主要硬件: cpu :cpu是一个计算机的运 ...

  2. 生成文件的MD5值

    import hashlib #########测试################# m = hashlib.md5() m.update(b"hello") m.update( ...

  3. ios兼容 iphoneX ios10 ios11

    假设你有一个固定位置的标题栏,你的iOS10的CSS可能是这样写的: header { position: fixed; top:; left:; right:; height: 44px; padd ...

  4. Redis原理

    RESP协议 支持tcp协议.基本数据类型,比如数组,字符串等,也可支持其他的通信场景. 模拟redis接收传输过来的set数据 //ServerSocket监听6379端口模拟redis publi ...

  5. Linux下Power Management开发总结

    本文作为一个提纲挈领的介绍性文档,后面会以此展开,逐渐丰富. 1. 前言 在 <开发流程>中介绍了PM开发的一般流程,重点是好的模型.简单有效的接口参数.可量化的测试环境以及可独性强的输出 ...

  6. odoo12.0 在Ubutu 18.04下环境的搭建

    sudo apt-get update sudo apt- postgresql nano virtualenv gcc python3.-dev libxml2-dev libxslt1-dev l ...

  7. [Oracle维护工程师手记]两表结合的MVIEW的告诉刷新

    对两表结合查询建立MVIEW,进行MVIEW的的高速刷新失败,如何处理? 例如: SQL> drop user u1 cascade; User dropped. SQL> grant d ...

  8. 软件工程(FZU2015) 赛季得分榜,第11回合(beta冲刺+SE总结)

    SE_FZU目录:1 2 3 4 5 6 7 8 9 10 11 12 13 积分规则 积分制: 作业为10分制,练习为3分制:alpha30分:beta30分 团队项目分=团队得分+个人贡献分 个人 ...

  9. mpvue-Vant Weapp踩坑记

    微信开发者工具:开发.调试和模拟运行微信小程序的最核心的工具了,所以必须安装 # 全局安装 vue-cli $ npm install --global vue-cli # 创建一个基于 mpvue- ...

  10. 数据标记系列——图像分割 & PolygonRNN++(一)

    当前大多数图像语义分割算法都是基于深度学习的方式,但是深度学习的效果很大程度上是依赖于大量训练数据的.目前的图像分割方法无非两种,一种是通过标注人员手动标注,如Cityscapes(提供无人驾驶环境下 ...