一天一个设计模式(二) -单例模式(Singleton)

前言

单例模式 (Singleton) 是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点。

正文

(一). 优缺点

Java单例模式 (Singleton) 是一种广泛使用的设计模式。单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在。一些管理器和控制器常被设计成单例模式。

1. 优点

  • 提供了对唯一实例的受控访问。
  • 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
  • 可以根据实际情况需要,在单例模式的基础上扩展做出双例模式,多例模式。

2. 缺点

  • 单例类的职责过重,里面的代码可能会过于复杂,在一定程度上违背了“单一职责原则”。
  • 如果实例化的对象长时间不被利用,会被系统认为是垃圾而被回收,这将导致对象状态的丢失。

(二). 具体实现

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。getInstance()的返回值是一个同一个对象的引用,并不是一个新的实例。单例模式 实现起来也很容易,以下给出六种实现方式:

1. 饿汉式

特点:线程安全,无法实现实例懒加载策略。

123456789
public class Singleton1 {    private final static Singleton1 singleton1 = new  Singleton1();    private Singleton1() {    }

public static Singleton1 getInstance() {        return singleton1;    }}

2. 懒汉式

特点:线程不安全,实现了实例懒加载策略。

1234567891011
public class Singleton2 {    private final static Singleton2 singleton2;    private Singleton2() {    }

public static Singleton2 getInstance() {        if (singleton2 == null)            singleton2 = new Singleton2();        return singleton2;    }}

3. 全局锁式

特点:线程安全,且实现了懒加载策略,但是线程同步时效率不高。

1234567891011
public class Singleton3 {    private final static Singleton3 singleton3;    private Singleton3() {    }

public synchronized static Singleton3 getInstance() {        if (singleton3 == null)            singleton3 = new Singleton3();        return singleton3;    }}

4. 静态代码块式

特点:线程安全,类主动加载时才初始化实例,实现了懒加载策略,且线程安全。

123456789101112
public class Singleton4 {    private final static Singleton4 singleton4;    private Singleton4() {    }    static {        singleton4 = new Singleton4();    }

public static Singleton4 getInstance() {        return singleton4;    }}

5. 双重校验锁式

特点:线程安全,且实现了懒加载策略,同时保证了线程同步时的效率。但是volatile强制当前线程每次读操作进行时,保证所有其他的线程的写操作已完成。volatile使得JVM内部的编译器舍弃了编译时优化,对于性能有一定的影响。

12345678910111213141516
public class Singleton5 {    private static volatile Singleton5 singleton5;    private Singleton5() {    }

public static Singleton5 getInstance() {        if (singleton5 == null) {            synchronized (Singleton5.class) {                if (singleton5 == null) {                    singleton5 = new Singleton5();                }            }        }        return singleton5;    }}

6. 静态内部类式【推荐】

特点:线程安全,不存在线程同步问题,且单例对象在程序第一次 getInstance() 时主动加载 SingletonHolder 和其 静态成员 INSTANCE,因而实现了懒加载策略。

123456789101112
public class Singleton6 {    private Singleton6() {    }

private static class SingletonHolder {        private static final Singleton6 INSTANCE = new Singleton6();    }

public static Singleton6 getInstance() {        return Singleton6.SingletonHolder.INSTANCE;    }}

7. 枚举方式【作者推荐】

特点:线程安全,不存在线程同步问题,且单例对象在枚举类型 INSTANCE 第一次引用时通过枚举的 构造函数 初始化,因而实现了懒加载策略。

1234567891011121314151617181920212223242526
public class Singleton7 {    private Singleton7() {    }

enum SingletonEnum {        INSTANCE;

private final Singleton7 singleton7;

private SingletonEnum() {            singleton7 = new Singleton7();        }    }

public static Singleton7 getInstance() {        return SingletonEnum.INSTANCE.singleton7;    }

public static void main(String[] args) {        IntStream.rangeClosed(0, 100).forEach(i -> new Thread() {            public void run() {                out.println(Singleton7.getInstance());            };        }.start());    }}

这种方式是Effective Java作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊。不过,由于JDK 1.5中才加入enum特性,用这种方式写不免让人感觉生疏。

测试代码如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
@FixMethodOrderpublic class SingletonTester {    protected final static int FROM = 0;    protected final static int TO = 1000;

protected static HashSet<Object> GLOBAL_SET = new HashSet<>();

static {        Runtime.getRuntime().addShutdownHook(new Thread() {            @Override            public void run() {                out.println();                // count                GLOBAL_SET.forEach((value) -> {                    out.println("Global [" + value + "]");                });            }        });    }

// testSingleton1    @Test    public void testSingleton1() throws Exception {        final HashSet<Object> localSet = new HashSet<>();        final CountDownLatch latch = new CountDownLatch(TO);        IntStream.range(FROM, TO).forEach(i -> new Thread() {            public void run() {                Singleton1 singleton = Singleton1.getInstance();                count(singleton);            }

protected void count(Singleton1 singleton) {                localSet.add(singleton);                out.println("Size of HashSet1 is: [" + localSet.size() + "]");                // 计数减1,释放线程                latch.countDown();            };        }.start());

// 等待子线程执行结束        latch.await();

synchronized (localSet) {            // count            localSet.forEach((value) -> {                out.println("[" + value + "]");                out.println();            });            GLOBAL_SET.addAll(localSet);        }    }

// testSingleton2    // testSingleton3    // testSingleton4    // testSingleton5    // testSingleton6    // testSingleton7}

测试结果截图如下,测试用例反映7种单例模式的方案都可以正常执行:

这里只演示其中一种单例方式,运行截图如下:


上图显示,通过 getInstance() 得到的实例全局唯一。对于其余六中方式,根据测试用例测试得到的结果一致,大家可以自行测试。

总结

本文总结了七种Java中实现单例模式的方法,其中使用双重校验锁静态内部类 和 枚举类的方式可以解决大部分问题。其中,极为推荐 静态内部类 和 枚举类 这两种实现方式。



欢迎关注技术公众号: 零壹技术栈

零壹技术栈

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

原文地址:https://www.cnblogs.com/ostenant/p/9695152.html

时间: 2024-12-26 21:34:00

一天一个设计模式(二) -单例模式(Singleton)的相关文章

设计模式之单例模式——Singleton

                    设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有公开的调用方法.而且,别的类不能实例化它,所以构造方法要设置为私有的. 单例模式的要点 一是某个类只能有一个实例: 二是它必须自行创建这个实例: 三是它必须自行向整个系统提供这个实例. 例如: 有一个"单例对象",而"客户甲"."客户乙" 和&quo

java设计模式之单例模式(Singleton)

Java设计模式之单例模式 单例模式是什么? 保证一个类仅有一个实例,并提供一个访问它的全局访问点. 单例模式如何来设计呢? 保证一个类只能有一个实例,那么我们不能无限制的new 来创建,因为我们知道,new一次就是一个新的对象,那么构造器只能私有化private -- 构造器私有化 构造器私有化了,问题又出现了,构造器私有化了,那么我们怎么来创建唯一的对象呢?-- 提供一个公有的方法/提供一个公有的静态属性 再分析一下,公有方法, 实例方法还是类方法呢?--公有的类方法(获取类实例) 依据以上

二十四种设计模式:单例模式(Singleton Pattern)

单例模式(Singleton Pattern) 介绍保证一个类仅有一个实例,并提供一个访问它的全局访问点. 示例保证一个类仅有一个实例. Singleton using System; using System.Collections.Generic; using System.Text; namespace Pattern.Singleton { /// <summary> /// 泛型实现单例模式 /// </summary> /// <typeparam name=&q

(转)设计模式之——单例模式(Singleton)的常见应用场景

单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此有些设计大师并把把其称为设计模式之一. 这里又不具体讲如何实现单例模式和介绍其原理(因为这方便的已经有太多的好文章介绍了),如果对单例模式不了解的可以先看下:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html .当然也可以自己搜索.

设计模式之——单例模式(Singleton)的常见应用场景(转):

单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此有些设计大师并把把其称为设计模式之一. 这里又不具体讲如何实现单例模式和介绍其原理(因为这方便的已经有太多的好文章介绍了),如果对单例模式不了解的可以先看下:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html . 好多没怎么使用过的人

设计模式之单例模式Singleton pattern

单例模式Singleton pattern 一种软件设计模式.在核心结构中只包含一个被称为单例的特殊类. 一个类只有一个对象实例,并且自行实例化向整个系统提供. 动机 一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务:一个系统只能有一个窗口管理器或文件系统:一个系统只能有一个计时工具或ID(序号)生成器.如在Windows中就只能打开一个任务管理器.如果不使用机制对窗口对象进行唯一化,将弹出多个窗口,如果这些窗口显示的内容完全一致,则是重复对象,浪费内存资源:如果这些窗口显示的内容不

大熊君说说JS与设计模式之------单例模式Singleton()

一,总体概要 1,笔者浅谈 顾名思义单例模式并不难理解,是产生一个类的唯一实例,在我们实际开发中也会使用到这种模式,它属于创建模式的一种,基于JS语言本身的语法特征, 对象直接量“{}”,也可以作为单例模式的一种表现形式,如下代码参考 1 function Foo(){ 2 this.bar = "Hello Singleton !" ; 3 } ; 4 var Singleton = { 5 instance : null , 6 getInstance : function(){

Android设计模式之单例模式 Singleton

一.概述 单例模式是设计模式中最简单的一种,但是它没有设计模式中的那种各种对象之间的抽象关系,所以有人不认为它是一种模式,而是一种实现技巧.单例模式就像字面的意思一样,提供一个只能自己实例化的实例,并且提供了一个全局的访问点.要达到这几点要求就要满足三点:私有构造函数(防止被别人实例化),静态私有自身对象(用来提供实例),静态公有的getInstance方法(用来创建和获取实例对象). 优缺点: 单例只允许自己建立一个实例,不需要频繁创建和销毁,可以节省内存加快对象的访问速度. 但是单例没有抽象

设计模式二 单例模式

0.基本定义 单例模式,保证一个类仅有一个实例,并提供一个访问它但全局访问点. 通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象.一个最好的办法,让类自身负责保存它的唯一实例. 这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法. 要求: 1.构造私有方法 2.定义一个实例,自己实例化 3.获取实例 spring 中默认bean加载都是单例. 1.实现方式 1.1.饿汉式 public class Emperor { private static