适用场景

保证一个类仅有一个实例,并提供一个访问它的全局访问点。例如只应该有一个文件系统来保证文件的正确访问。
JDK中 java.lang.Runtime#getRuntime()就是一个典型应用:

Every Java application has a single instance of classRuntime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from thegetRuntime method.

An application cannot create its own instance of this class.

实现及安全性分析

实现一、饿汉式(线程安全)

package com.wenc.designpattern;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Singleton {/*** 类实例变量* private:保证变量不能被使用线程直接获取* static:instance由JVM在类初始化阶段执行,即在类被加载后并且被线程使用之前完成。* 由于JVM将在初始化期间获得一个锁,并且每个线程都至少获取一次这个锁以确保这个类已经加载,* 因此在静态初始化期间,内存写入操作自动对所有线程科技那。因此无论是在构造期间还是被引用时,* 静态初始化的对象都不需要显示同步。然而,这个队则仅适用于在构造时的状态,* 如果对象可变,那么在读线程和写线程之间仍需要同步。*/private static Singleton instance = new Singleton();/*** 访问入口* @return 唯一类实例*/public static Singleton getSingleton() {return instance;}/*** private:保证使用线程不能直接初始化实例,仅能通过getSingleton唯一入口获取*/private Singleton() {System.out.println("singleton initial~");}public static void main(String[] args) {System.out.println("main begin");ExecutorService exec = Executors.newFixedThreadPool(10);Runnable run = new Runnable() {public void run() {//打印获得的类实例信息,用于验证是否唯一System.out.println(Singleton.getSingleton());}} ;System.out.println("go~");exec.execute(run);exec.execute(run);System.out.println("done~");}
}

输出:

singleton initial~
main begin
go~
com.wenc.designpattern.Singleton@50f7270b
done~
com.wenc.designpattern.Singleton@50f7270b

优点: Singleton类加载时(第一个使用者线程调用getInstance时)初始化唯一类实例; 利用JVM类初始化机制保证线程安全,无需显示同步;

缺点: 类加载时就占用内存空间,对于内存敏感的应用不利。
java.lang.Runtime#getRuntime()正是用“饿汉式”实现。

实现二、饱汉式(线程安全)

package com.wenc.designpattern;import java.util.concurrent.Executor;
import java.util.concurrent.Executors;public class SingletonHolder {/*** @author 文超* 内部类,触发时才加载*/public static class Singleton {private static Singleton instance = new Singleton();private Singleton() {System.out.println(Thread.currentThread() + " : singleton initial~");  }}/*** 外部类调用该方法时触发Singleton类加载* @return 类实例*/private static Singleton getInstance() {return Singleton.instance;}public static void main(String[] args) {System.out.println("main begin");Executor exec = Executors.newFixedThreadPool(10);Runnable run = new Runnable() {public void run() {System.out.println(Thread.currentThread() + " : " + SingletonHolder.getInstance());}} ;System.out.println("go~");exec.execute(run);exec.execute(run);System.out.println("done~");}
}

输出:

main begin
go~
done~
Thread[pool-1-thread-1,5,main] : singleton initial~
Thread[pool-1-thread-1,5,main] : com.wenc.designpattern.SingletonHolder$Singleton@2c87f1ce
Thread[pool-1-thread-2,5,main] : com.wenc.designpattern.SingletonHolder$Singleton@2c87f1ce

与实现一的区别:singleton initial日志输出在main begin之后,说明main方法使用到singleton.instance时才初始化了内部类Singleton,比实现一更晚。

实现三、双重检查加锁DCL(线程不安全)

package com.wenc.designpattern;import java.util.concurrent.Executor;
import java.util.concurrent.Executors;public class WrongSingleton {//可加volatile使instance可见,保证使用instance时其已被完整构造,实现线程安全private static WrongSingleton instance;private WrongSingleton() {System.out.println("WrongSingleton()");}public static WrongSingleton getInstance() {if(instance == null) {synchronized(WrongSingleton.class) {if(instance == null) {//下面这句包含两个方面:1、对象各域的初始化;2、发布引用,即引用赋值给instance.//如果无法确保发布共享引用的操作在另一个线程加载该共享引用之前执行,那么对新对象引用的写入操作//将于对象中各个域的写入操作重排序。造成使用线程仅看到一个部分构造的对象。instance = new WrongSingleton();}}}return instance;}public static void main(String[] args) throws InterruptedException {//Executor exec = new NewThreadExecutor();Executor exec = Executors.newFixedThreadPool(10);Runnable run = new Runnable() {public void run() {System.out.println(Thread.currentThread() + " : " + WrongSingleton.getInstance());}} ;System.out.println("go~");exec.execute(run);//Thread.sleep(1000);exec.execute(run);System.out.println("done~");}
}

输出:

go~
done~
WrongSingleton()
Thread[pool-1-thread-2,5,main] : com.wenc.designpattern.WrongSingleton@12b68fd4
Thread[pool-1-thread-1,5,main] : com.wenc.designpattern.WrongSingleton@12b68fd4

本例输入并未输出类实例部分构造的结果

改进:如果将instance变量加上volatile属性,则能保证完整构造,实现线程安全。

总结

建议采用实现二,使用时才初始化且保证线程安全,实现安全发布。

java单例模式及其安全发布(含饿汉式、饱汉式和错误示例)相关推荐

  1. java单例模式之懒汉模式和饿汉模式

    两者共同点 // * 1,定义私有的构造方法,禁止外部直接创建实例// * <p/>// * 2,内部自己创建好实例,私有属性(不建议在外部直接调用我们的成员变量)// * <p/& ...

  2. 如何编写单例模型?(饿汉和饱汉模式)

    package cn.kgc.tools;import java.io.IOException; import java.io.InputStream; import java.util.Proper ...

  3. Java单例模式--懒汉式和饿汉式(Demo)

    你好我是辰兮,很高兴你能来阅读,本篇文章为大家讲解Java单例模式,相关的更多面试知识已经提前整理好文章可以阅读学习,分享获取新知,希望对Java初学者有帮助. 1.JAVA基础面试常考问题 : JA ...

  4. Java单例模式--------懒汉式和饿汉式

    Java单例模式--------懒汉式和饿汉式 单件模式用途: 单件模式属于工厂模式的特例,只是它不需要输入参数并且始终返回同一对象的引用. 单件模式能够保证某一类型对象在系统中的唯一性,即某类在系统 ...

  5. 单例模式可以分为懒汉式和饿汉式:     懒汉式单例模式:在类加载时不初始化。     饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。

     单例模式可以分为懒汉式和饿汉式: 懒汉式单例模式:在类加载时不初始化. 饿汉式单例模式:在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快.

  6. 单例模式的原理/懒汉模式/饿汉模式以及不同版本的单例模式程序

    单例模式 单例模式定义 懒汉模式与饿汉模式 懒汉模式 饿汉模式 懒汉模式和饿汉模式的区别 懒汉模式的不同版本 版本一 版本二 版本三 版本四 单例模式定义 保证一个类仅有一个实例,并提供一个该实例的全 ...

  7. C# 设计模式之单例模式(懒汉模式、饿汉模式、静态内部类模式)

    C# 设计模式之单例模式(懒汉模式.饿汉模式.静态内部类模式) 应用场景:在整个软件运行生命周期内,一个类只允许一次实例化,例如数据库连接池的连接对象创建:通过使用单例模式来避免反复创建连接对象,从而 ...

  8. Java面试题集锦(含答案)

    Java面试题集锦 Java基础 1.ArrayList.Vector和LinkedList有什么区别? 答:ArrayList底层实现是数组,查找快,增删慢,线程不安全 Vector 和ArrayL ...

  9. Java单例模式个人总结(实例变量和类变量)

    Java单例模式 背景知识:Static关键字. 在对于定义类的变量,分为两种,是否具有static修饰的变量: 没有static修饰的变量,通过类的实例化(对象)引用,改变量称为实例变量: 使用st ...

最新文章

  1. python-01实现考勤表添加与删除
  2. android弹出窗口的实现(PopupWindow)
  3. python模块与包
  4. HarmonyOS之基础环境和应用开发流程
  5. Maven软件的下载安装
  6. 基于TensorFlow Lite的人声识别在端上的实现
  7. MySQL的用户表(user)
  8. python 函数的参数对应
  9. SQL Server 透视与逆透视转换解析
  10. javascript 遍历数组的常用方法(迭代、for循环 、for… in、for…of、foreach、map、filter、every、some,findindex)
  11. Atitit.html解析器的选型 jsoup nsoup ,java c# .net 版本
  12. 计算机竞赛acm试题,ACM-ICPC 2018 总决赛赛题
  13. c语言 常量和变量 ppt,c语言常量与变量.ppt
  14. python中的array函数作用_Python中的numpy常用函数整理
  15. 怎么处理视频声音变声?视频声音变声处理软件哪个好?
  16. win11怎么看电脑显卡信息
  17. 【源码+图片素材+详细教程】Java游戏开发_Java开发经典游戏飞翔的小鸟_飞扬的小鸟_Java游戏项目Flappy Bird像素鸟游戏_Java课程设计项目
  18. 按图搜索淘宝、天猫、1688商品API。(拍立淘API)
  19. Shell脚本导出导入MySQL建表语句
  20. 【Matlab学习】

热门文章

  1. Android知识补充(Android学习笔记)
  2. CAD文件进行自动化三维建模
  3. 江南爱软装十大品牌 软装的风格你都知道多少?
  4. CPU规则命名和选型
  5. 关于STM32CubeMX软件画面重影问题
  6. Finder持续无响应怎么办?Finder无响应解决教程
  7. OUTLOOK如何将几百个联系人添加到联系人组里面(不用一个一个添加),这里有妙招
  8. 【人事】短期找不到工作怎么办
  9. Java实现一行代码生成二维码,可传输到前端展示,可自定义二维码样式,可设置图片格式,可对二维码添加图片,可对二维码添加文字,可以设置二维码大小、字体大小、字体颜色、边框颜色、边框大小等等
  10. python-list对象内置方法