单例模式的七种实现-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 ...
随机推荐
- windows环境下安装yaf框架
windows环境下安装yaf框架 在windows下安装yaf框架 准备工作: php环境(过程略,wamp,xampp,phpstudy都行,php版本大于5.3) git工具(需要从github ...
- 3.20 总结 java程序流程控制
- JRE与JDK简介
如何进行 Java 开发: JRE: JDK:
- jdk的安装及环境变量的配置
一.JDK的下载 1.首先打开JDK的官网(点击打开链接),找到JAVA SE 7u71/72中的JDK,选择Download 2.然后如下图,选择Accept License Agreement,则 ...
- 通过secureCRT连接虚拟机VMware workstation问题记录
很急没有使用虚拟机了,今天再登录的时候,发现用secureCRT连接不上VMware workstation 1.连接步骤: 1)打开secureCRT,点击+ 新建一个连接 2)按照流程一步一步配置 ...
- C# 远程获取图片二进制
直接上代码, 紧做记录. public byte[] GetByteByImgUrl() { System.Net.WebRequest webreq = System.Net.WebRequest. ...
- Neutron local network 学习
local network 的特点是不会与宿主机的任何物理网卡相连,也不关联任何的 VLAN ID. 对于每个 local netwrok,ML2 linux-bridge 会创建一个 bridg ...
- 2019微信公开课 同行With Us 听课笔记及演讲全文
[2019WeChat 微信公开课] 产品理念: 微信启动页 一个小人站在地球前面,每个人都有自己的理解和解读 所谓异类,表示与别人与众不同,即优秀的代名词. 微信的与众不同体现在尊重用户对产品的感受 ...
- [转帖]golang操作mysql使用总结
golang操作mysql使用总结 https://www.cnblogs.com/hanyouchun/ 讲解的很详细~ 前言 Golang 提供了database/sql包用于对SQL数据库的访问 ...
- javascript高级排序算法之快速排序(快排)
javascript高级排序算法之快速排序(快排)我们之前讨论了javascript基本排序算法 冒泡排序 选择排序 插入排序 简单复习: 冒泡排序: 比较相邻的两个元素,如果前一个比后一个大,则交换 ...