01设计模式——单例模式

前言:以下总结来自龙哥---左潇龙博客。

总结的很到位,附上博客链接:http://www.cnblogs.com/zuoxiaolong/p/pattern2.html

目的:尽可能的节约内存空间,减少无谓的GC消耗,并且使应用可以正常运作。

如何判断单例:这些类,在应用中如果有两个或者两个以上的实例会引起错误,又或者我换句话说,就是这些类,在整个应用中,同一时刻,有且只能有一种状态。

 1 /**
 2  * 简单单例模式
 3  * 目的,是为了尽可能的节约内存空间,减少无谓的GC消耗,并且使应用可以正常运作。
 4  * 一般一个类能否做成单例,最容易区别的地方就在于,这些类,在应用中如果有两个或者两个以上的实例会引起错误,又或者我换句话说,就是这些类,在整个应用中,同一时刻,有且只能有一种状态。
 5  * 1.静态实例,带有static关键字的属性在每一个类中都是唯一的。
 6  * 2.限制客户端随意创造实例,即私有化构造方法,此为保证单例的最重要的一步。
 7  * 3.给一个公共的获取实例的静态方法,注意,是静态的方法,因为这个方法是在我们未获取到实例的时候就要提供给客户端调用的,所以如果是非静态的话,那就变成一个矛盾体了,因为非静态的方法必须要拥有实例才可以调用。
 8  * 4.判断只有持有的静态实例为null时才调用构造方法创造一个实例,否则就直接返回。
 9  */
10 public class Singleton {
11
12     // 一个静态的实例
13     private static Singleton singleton;
14
15     // 私有化构造函数
16     private Singleton() {
17         super();
18     }
19
20     // 给出一个公共的静态方法返回一个单一实例
21     public static Singleton getInstance() {
22         if (singleton == null) {
23             singleton = new Singleton();
24         }
25         return singleton;
26     }
27
28     // 验证单例
29     public static void main(String[] args) {
30         Singleton instance1 = Singleton.getInstance();
31         Singleton instance2 = Singleton.getInstance();
32         System.out.println(instance1);
33         System.out.println(instance2);
34     }
35 }
 1 import java.util.Collections;
 2 import java.util.HashSet;
 3 import java.util.Set;
 4 import java.util.concurrent.ExecutorService;
 5 import java.util.concurrent.Executors;
 6 import java.util.concurrent.TimeUnit;
 7
 8 /**
 9  * 验证Singleton线程不安全
10  */
11 public class TestSingleton {
12
13     private volatile boolean lock;
14
15     public boolean isLock() {
16         return lock;
17     }
18
19     public void setLock(boolean lock) {
20         this.lock = lock;
21     }
22
23     /**
24      * 当并发访问的时候,第一个调用getInstance方法的线程A,在判断完singleton是null的时候,线程A就进入了if块准备创造实例,
25      * 但是同时另外一个线程B在线程A还未创造出实例之前,就又进行了singleton是否为null的判断,这时singleton依然为null,
26      * 所以线程B也会进入if块去创造实例,这时问题就出来了,有两个线程都进入了if块去创造实例,结果就造成单例模式并非单例。
27      * */
28     public static void main(String[] args) throws InterruptedException {
29         final Set<String> instanceSet = Collections.synchronizedSet(new HashSet<String>());
30         final TestSingleton lock = new TestSingleton();
31         lock.setLock(true);
32         ExecutorService executorService = Executors.newCachedThreadPool();
33         for (int i = 0; i < 100; i++) {
34             executorService.execute(new Runnable() {
35
36                 public void run() {
37                     while (true) {
38                         if (!lock.isLock()) {
39                             Singleton singleton = Singleton.getInstance();
40                             instanceSet.add(singleton.toString());
41                             break;
42                         }
43                     }
44                 }
45             });
46         }
47         // main线程睡眠了两次
48         Thread.sleep(5000);// 第一次是为了给足够的时间让100个线程全部开启,
49         lock.setLock(false);
50         Thread.sleep(5000);// 第二个是将锁打开以后,保证所有的线程都已经调用了getInstance方法。
51         System.out.println("------并发情况下我们取到的实例------");
52         for (String instance : instanceSet) {
53             System.out.println(instance);
54         }
55         executorService.shutdown();
56         executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
57         System.out.println("程序运行结束!!!");
58     }
59 }
 1 /**
 2  * 就是将整个获取实例的方法同步,
 3  * 这样在一个线程访问这个方法时,其它所有的线程都要处于挂起等待状态,倒是避免了刚才同步访问创造出多个实例的危险,
 4  * 这样的设计实在是糟糕透了,这样会造成很多无谓的等待。
 5  */
 6 public class BadSynchronizedSingleton {
 7
 8     // 一个静态的实例
 9     private static BadSynchronizedSingleton singleton;
10
11     // 私有化构造函数
12     private BadSynchronizedSingleton() {
13         super();
14     }
15
16     // 给出一个公共的静态方法返回一个单一实例
17     public static BadSynchronizedSingleton getInstance() {
18         if (singleton == null) {
19             singleton = new BadSynchronizedSingleton();
20         }
21         return singleton;
22     }
23
24     // 验证单例
25     public static void main(String[] args) {
26         BadSynchronizedSingleton instance1 = BadSynchronizedSingleton.getInstance();
27         BadSynchronizedSingleton instance2 = BadSynchronizedSingleton.getInstance();
28         System.out.println(instance1);
29         System.out.println(instance2);
30     }
31 }
 1 /**
 2  * 双重加锁
 3  * JVM创建新的对象时,主要要经过三步。
 4  * 1.分配内存
 5  * 2.初始化构造器
 6  * 3.将对象指向分配的内存的地址
 7  * 这种顺序在上述双重加锁的方式是没有问题的,因为这种情况下JVM是完成了整个对象的构造才将内存的地址交给了对象。
 8  * 但是如果2和3步骤是相反的(2和3可能是相反的是因为JVM会针对字节码进行调优,而其中的一项调优便是调整指令的执行顺序),就会出现问题了。
 9  * 因为这时将会先将内存地址赋给对象,针对上述的双重加锁,就是说先将分配好的内存地址指给synchronizedSingleton,
10  * 然后再进行初始化构造器,这时候后面的线程去请求getInstance方法时,会认为synchronizedSingleton对象已经实例化了,直接返回一个引用。
11  * 如果在初始化构造器之前,这个线程使用了synchronizedSingleton,就会产生莫名的错误。
12  */
13 public class SynchronizedSingleton {
14
15     // 一个静态的实例
16     private static SynchronizedSingleton singleton;
17
18     // 私有化构造函数
19     private SynchronizedSingleton() {
20         super();
21     }
22
23     // 给出一个公共的静态方法返回一个单一实例
24     /**
25      *  假设我们去掉同步块中的是否为null的判断,
26      *  有这样一种情况,假设A线程和B线程都在同步块外面判断了synchronizedSingleton为null,
27      *  结果A线程首先获得了线程锁,进入了同步块,
28      *  然后A线程会创造一个实例,此时synchronizedSingleton已经被赋予了实例,
29      *  A线程退出同步块,直接返回了第一个创造的实例,
30      *  此时B线程获得线程锁,也进入同步块,此时A线程其实已经创造好了实例,B线程正常情况应该直接返回的,
31      *  但是因为同步块里没有判断是否为null,直接就是一条创建实例的语句,
32      *  所以B线程也会创造一个实例返回,此时就造成创造了多个实例的情况。
33      */
34     public static SynchronizedSingleton getInstance() {
35         if (singleton == null) {
36             synchronized (SynchronizedSingleton.class) {
37                 if (singleton == null) {
38                     singleton = new SynchronizedSingleton();
39                 }
40             }
41         }
42         return singleton;
43     }
44
45     // 验证单例
46     public static void main(String[] args) {
47         SynchronizedSingleton instance1 = SynchronizedSingleton.getInstance();
48         SynchronizedSingleton instance2 = SynchronizedSingleton.getInstance();
49         System.out.println(instance1);
50         System.out.println(instance2);
51     }
52
53 }
 1 /**
 2  * 比较标准的单例模式
 3  * 一个类的静态属性只会在第一次加载类时初始化,这是JVM帮我们保证的,所以我们无需担心并发访问的问题。
 4  * 所以在初始化进行一半的时候,别的线程是无法使用的,因为JVM会帮我们强行同步这个过程。
 5  * 另外由于静态变量只初始化一次,所以singleton仍然是单例的。
 6  *
 7  * 1.Singleton最多只有一个实例,在不考虑反射强行突破访问限制的情况下。
 8  * 2.保证了并发访问的情况下,不会发生由于并发而产生多个实例。
 9  * 3.保证了并发访问的情况下,不会由于初始化动作未完全完成而造成使用了尚未正确初始化的实例。
10  *
11  */
12 public class Singleton {
13
14     private Singleton() {
15         super();
16     }
17
18     public static Singleton getInstance() {
19         return SingletonInstance.instance;
20     }
21
22     private static class SingletonInstance {
23         static Singleton instance = new Singleton();
24     }
25
26 }
 1 /**
 2  * 饿汉式
 3  * 这种方式最主要的缺点
 4  *     就是一旦我访问了Singleton的任何其他的静态域,就会造成实例的初始化,而事实是可能我们从始至终就没有使用这个实例,造成内存的浪费。
 5  *  不过在有些时候,直接初始化单例的实例也无伤大雅,对项目几乎没什么影响,比如我们在应用启动时就需要加载的配置文件等,就可以采取这种方式去保证单例。
 6  */
 7 public class Singleton {
 8
 9     // 一个静态的实例
10     private static Singleton singleton = new Singleton();
11
12     // 私有化构造函数
13     private Singleton() {
14         super();
15     }
16
17     // 给出一个公共的静态方法返回一个单一实例
18     public static Singleton getInstance() {
19         return singleton;
20     }
21
22     // 验证单例
23     public static void main(String[] args) {
24         Singleton instance1 = Singleton.getInstance();
25         Singleton instance2 = Singleton.getInstance();
26         System.out.println(instance1);
27         System.out.println(instance2);
28     }
29
30 }

原文地址:https://www.cnblogs.com/hilter/p/8797467.html

时间: 2024-10-03 22:39:23

01设计模式——单例模式的相关文章

设计模式 - 单例模式之多线程调试与破坏单例

前言 在之前的 设计模式 - 单例模式(详解)看看和你理解的是否一样? 一文中,我们提到了通过Idea 开发工具进行多线程调试.单例模式的暴力破坏的问题:由于篇幅原因,现在单独开一篇文章进行演示:线程不安全的单例在多线程情况下为何被创建多个.如何破坏单例. 如果还不知道如何使用IDEA工具进行线程模式的调试,请先阅读我之前发的一篇文章: 你不知道的 IDEA Debug调试小技巧 一.线程不安全的单例在多线程情况下为何被创建多个 首先回顾简单线程不安全的懒汉式单例的代码以及测试程序代码: /**

php设计模式——单例模式(Singleton)

二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 谷歌的Android设备 华为的Android设备 IOS只属于苹果公司 IOS只属于苹果公司 1 <?php 2 3 /* 4 * php

Android 设计模式-单例模式

Android 设计模式-单例模式 什么情况下需要单例模式? 一些类提供公共功能供别人调用,本身不会处理业务逻辑 类会被许多类和线程调用 设计单例模式 public class Singleton{ private static Singleton mSingleton; private Singleton(){ } public static Singleton getInstance(){ if(mSingleton == null){ mSingleton = new Singleton(

设计模式--单例模式(学习笔记)

定义:      单例模式:保证一个类只有一个实例,并且提供一个访问它的全局访问点.(<大话设计模式定义>): 单例模式分为两种: 1>饿汉式单例类:自己被加载时就将自己实例化. 例子:       private SingleExample() {           } private static SingleExample singleExample=new SingleExample(); @Override protected SingleExample getInstanc

简易的设计模式——单例模式

定义 单例模式是一种保证一个类中只有一个实例对象的软件设计模式.它需要确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 这个还是比较好理解的,一个国家只能有一个国王,不可以出现第二个,所有的人都只能来膜拜这个伟大的国王. 下面直接上代码. 实现 国王类 public class King { //确保只有一个国王 private static final King king=new King(); //保证不再产生新的国王了 private King(){} //这个国家的人通过

Java设计模式の单例模式

-------------------------------------------------- 目录 1.定义 2.常见的集中单例实现 a.饿汉式,线程安全 但效率比较低 b.单例模式的实现:饱汉式,非线程安全 c.饱汉式,线程安全简单实现 d.线程安全 并且效率高  单例模式最优方案 3.总结 a.使用枚举的单例模式 b.使用枚举,static处调用,初始化一次 -------------------------------------------------- 1.定义 确保一个类只有

c#设计模式-单例模式(面试题)

c#设计模式-单例模式 单例模式三种写法: 第一种最简单,但没有考虑线程安全,在多线程时可能会出问题, public class Singleton { private static Singleton _instance = null; private Singleton(){} public static Singleton CreateInstance() { if(_instance == null) { _instance = new Singleton(); } return _in

设计模式--单例模式之Lock

1.为什么用Lock及关键知识 当我们使用线程的时候,效率最高的方式当然是异步,即个个线程同时运行,其间互不依赖和等待.当不同的线程都需要访问某个资源的时候,就需要同步机制了,也就是说当对同一个资源进行读写的时候,我们要使该资源在同一时刻只能被同一个线程操作,以确保每个操作都是有效即时的,也即保证其操作的原子性.lock是C#中最常用的同步方式,格式为lock(objectA){codeB} . lock(objectA){codeB}表示意义: 1. objectA被lock了吗?没有则由我来

c#设计模式-单例模式【转】

单例模式三种写法: 第一种最简单,但没有考虑线程安全,在多线程时可能会出问题 public class Singleton { private static Singleton _instance = null; private Singleton(){} public static Singleton CreateInstance() { if(_instance == null) { _instance = new Singleton(); } return _instance; } } 第