背景:最近在学习韩老师的笔记时候发现不是很了解单例和多例,于是通过网上查找资料的方式去学习。

设计模式:最佳的实践,是软件开发人员在软件开发过程中面临一般解决方案,也就是开发的经验总结。

单例模式(Singleton):是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

单例类的特点:

  • 单例类只能有一个实例。
  • 单例类必须自己创建自己的唯一实例。
  • 单例类必须给所有其他对象提供这一实例。
  • 单例类的构造方法私有。(避免外部利用构造方法直接创建多个实例

多例模式(Multiton):作为对象的创建模式,多例模式中的多例类可以有多个实例,而且多例类必须自己创建、管理自己的实例,并向外界提供自己的实例。

多例类的特点

  • 多例类可有多个实例。
  • 多例类必须自己创建,管理自己的实例,并向外提供自己的实例。
  • 根据是否有实例上限分为有上限的多例类和无上限的多例类。

多例的单例模式的比较:

  • 单例模式和多例模式属于对象模式。
  • 它们都不对外提供构造方法,即构造方法都为私有。
  • 单例模式的对象在整个系统中只有一份,多例模式可以有多个实例。
  • 单例是所有的请求都用一个对象来处理,比如service和dao层的对象通常都是单例的,而多例是每个请求用一个新的对象来处理,比如action;
  • 单例可以节约CPU和内存,多例可以防止并发问题,即一个请求改变了对象的状态,此时对象处理另一个请求,而之前的请求对象状态的改变导致对象对另一个请求的错误处理,对象中含有可改变的状态时,则用多例反之单例。

单例的实现:

创建一个 SingleObject 类。SingleObject 类有它的私有构造函数和本身的一个静态实例。SingleObject 类提供了一个静态方法,供外界获取它的静态实例。

package ecut.enums;

public class SingleObject {

    // 创建 SingleObject 的一个对象
private static SingleObject instance = new SingleObject(); // 让构造函数为 private,这样该类就不会被实例化
private SingleObject() {
} // 获取唯一可用的对象
public static SingleObject getInstance() {
return instance;
} public void showMessage() {
System.out.println("Hello World!");
}
}

在SingletonPatternDemo中使用 SingleObject 类来获取 SingleObject唯一 对象。

package ecut.enums;

public class SingletonPatternDemo {
public static void main(String[] args) { // 不合法的构造函数
// 编译时错误:构造函数 SingleObject() 是不可见的
// SingleObject object = new SingleObject(); // 获取唯一可用的对象
SingleObject object = SingleObject.getInstance(); // 显示消息
object.showMessage();
}
}

运行结果如下:

Hello World!

 单例的几种实现方法:

1、懒汉式,线程不安全

是lazy初始化(懒加载,在需要的时候才创建单例对象分配内存,而不是随着软件系统的运行或者当类被加载器加载的时候就创建),多线程不安全,容易实现,没有加锁synchronized,因此不支持多线程。

package ecut.enums;

/**
* "懒汉"式,线程不安全实现单例 ( Singleton )
*/
public class Sun { // 1、将所有构造方法私有 (无法在类外正常创建该类实例、该类无子类)
private Sun(){
} // 3、提供一个静态变量缓存那个惟一的实例
private static Sun s ; // 2、通过静态方法来获得该类的惟一的实例
public static Sun getInstance() {
if( s == null ) {
s = new Sun();
}
return s ;
}
}

2、懒汉式,线程安全

是lazy初始化,多线程安全,容易实现,具备很好的lazy loading,能够在多线程中很好的工作,但是效率低,99%情况下不需要同步。

优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

package ecut.enums;

/**
* "懒汉"式,线程安全实现单例 ( Singleton )
*/
public class Sun { // 1、将所有构造方法私有 (无法在类外正常创建该类实例、该类无子类)
private Sun(){
} // 3、提供一个静态变量缓存那个惟一的实例
private static Sun s ; // 2、通过静态方法来获得该类的惟一的实例,synchronized 关键字声明的方法同一时间只能被一个线程访问。
public static synchronized Sun getInstance() {
if( s == null ) {
s = new Sun();
}
return s ;
}
}

使用synchronized关键字修饰静态方法达到线程安全的原理: 对共享资源进行访问的方法定义中加上synchronized关键字修饰,此方法称为同步方法,可以简单理解成对此方法进行了加了内置锁,内置锁是当前的Class字节码对象,多线程环境下,当执行此方法时,首先都要获得此同步锁(且同时最多只有一个线程能够获得),只有当线程执行完此同步方法后,才会释放锁对象,其他的线程才有可能获取此同步锁,实现了线程同步。同步了线程就安全。

线程同步:同步就是协同步调,按预定的先后次序进行运行。

线程安全:多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的。

内置锁: 在Java中任何一个对象都可以用作于同步锁,而这些锁就是内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁,内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁。

内置锁在Java语言中的表现:多线程的锁,其实本质上就是给一块内存空间的访问添加访问权限,因为Java中是没有办法直接对某一块内存进行操作的,又因为Java是面向对象的语言,一切皆对象,所以具体的表现就是某一个对象承担锁的功能,每一个对象都可以是一个锁。内置锁,使用方式就是使用 synchronized 关键字,synchronized 方法或者 synchronized 代码块。

3、饿汉式

不是lazy初始化,多线程安全,容易实现,比较常用,但容易产生垃圾对象。

优点:比较常用,但容易产生垃圾对象。
缺点:类加载时就初始化,浪费内存(饿汉式单例在类被加载时就创建单例对象并且长驻内存,不管你需不需要它;如果单例类占用的资源比较多,就会降低资源利用率以及程序的运行效率)。

它基于 classloder 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

package ecut.enums;

/**
* "饿汉"式实现单例 ( Singleton )
*/
public class Moon { // 1、将所有构造方法私有 (无法在类外正常创建该类实例、该类无子类)
private Moon(){
} // 3、提供一个静态变量缓存那个惟一的实例
private static final Moon MOON = new Moon(); // 2、通过静态方法来获得该类的惟一的实例
public static Moon getInstance() {
return MOON ;
}
}

4、双检锁/双重校验(DCL,即double-checked locking)

jdk1.5起,是lazy初始化,多线程安全,实现较复杂,这种方式采用双锁机制,安全且在多线程情况下能保持高性能。getInstance() 的性能对应用程序很关键。

package ecut.enums;

/**
*
* 双检锁实现单例 ( Singleton )
*
*/
public class Singleton {
/*volatile:修饰成员变量在被每次被线程访问时,都强制从共享内存中读取该成员变量的值,而且当成员变量发生变化时,会强制线程将变化值写到共享内存,
这样在任何时刻两个不同的线程总是看到某个成员变量的同一个值。*/
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}

5、登记式/静态内部类

是lazy初始化,多线程安全。

利用了ClassLoader的机制保证了线程安全;不同的是,饿汉式在类被加载时就创建了一个实例对象,而静态内部类即使类被加载也不会创建单例对象,除非调用里面的getInstance()方法。因为当Singleton类被加载时,其静态内部类SingletonHolder没有被主动使用。只有当调用getInstance方法时,才会装载SingletonHolder类,从而实例化单例对象。这样,通过静态内部类的方法就实现了lazy loading,很好地将懒汉式和饿汉式结合起来,既实现延迟加载,保证系统性能,也能保证线程安全。

public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

6、枚举

jdk1.5起,不是lazy初始化,多线程安全,容易实现,这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}

多例的实现:

package ecut.enums;

/**
* 实现多例 ( Multiton )
*/
public class Season { private int index ;
private String name ; public static final Season SPRING = new Season( 1 , "春暖花开时" );
public static final Season SUMMER = new Season( 2 , "炎炎夏日" );
public static final Season AUTUMN = new Season( 3 , "又该吃月饼了" );
public static final Season WINTER = new Season( 4 , "美丽冻人" ); private Season(int index, String name) {
super();
this.index = index;
this.name = name;
} public int getIndex() {
return index;
} public String getName() {
return name;
} }

待解决问题:

ClassLoader机制

参考博客链接:

http://www.runoob.com/design-pattern/singleton-pattern.html

https://www.2cto.com/kf/201404/289810.html

http://blog.sina.com.cn/s/blog_70c2024b0102wxs0.html

http://blog.sina.com.cn/s/blog_6f5e71b30102xeqh.html

https://www.cnblogs.com/xyhuangjinfu/p/6505329.html

https://www.cnblogs.com/IUbanana/p/7112296.html

转载请于明显处标明出处:

http://www.cnblogs.com/AmyZheng/p/8443914.html

Java单例和多例的更多相关文章

  1. java单例的几种实现方法

    java单例的几种实现方法: 方式1: public class Something { private Something() {} private static class LazyHolder ...

  2. java单例类/

    java单例类  一个类只能创建一个实例,那么这个类就是一个单例类 可以重写toString方法 输出想要输出的内容 可以重写equcal来比较想要比较的内容是否相等 对于final修饰的成员变量 一 ...

  3. java单例-积木系列

    一步步知识点归纳吧,把以前似懂非懂,了解表面,知道点不知道面的知识归一下档.   懒汉式单例: 私有化构造函数,阻止外界实例话对象,调用getInstance静态方法,判断是否已经实例化. 为什么是懒 ...

  4. Java单例类的简单实现

    对于java新手来说,单例类给我的印象挺深,之前一道web后台笔试题就是写单例类.*.*可惜当时不了解. 在大部分时候,我们将类的构造器定义成public访问权限,允许任何类自由创建该类的对象.但在某 ...

  5. 【Java学习笔记之三十】详解Java单例(Singleton)模式

    概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例.饿汉式单例.登记式单例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建 ...

  6. 转:java单例设计模式

    本文转自:http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html 单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton ...

  7. 熟悉的味道——从Java单例写到C++单例

    设计模式中,单例模式是常见的一种.单例模式需要满足以下两个条件: 保证一个类只能创建一个示例: 提供对该实例的全局访问点. 关于单例最经典的问题就是DCL(Double-Checked Lock),今 ...

  8. java单例五种实现模式梳理

    java单例五种实现模式 饿汉式(线程安全,调用效率高,但是不能延时加载) 一上来就把单例对象创建出来了,要用的时候直接返回即可,这种可以说是单例模式中最简单的一种实现方式.但是问题也比较明显.单例在 ...

  9. JAVA单例实现方式(常用)

    JAVA单例实现方式(常用) public class Singleton { // Q1:为什么要使用volatile关键字? private volatile static Singleton u ...

随机推荐

  1. 12day echo {1..100} << 三剑客命令

    04. 直接编辑文件命令方法 a 如何直接在文件中添加单行内容 echo "oldboy" >>/oldboy/oldboy.txt 补充: echo命令用法说明: 0 ...

  2. 格式化输出_python

    一.直接使用 +a='chen'b='xiao'c='zan'print(a+b+c) 二.利用占位符 %%s:占位符:%d:整数:%x:十六进制:%f:浮点数(默认6位小数)特别注意浮点数: 指定长 ...

  3. centos7 sshpass 用法详解

    可以参考文章:https://www.cnblogs.com/kaishirenshi/p/7921308.html 安装方式直接通过yum 安装 yum -y install sshpass 常用的 ...

  4. gerp 查找, sed 编辑, awk 根据内容分析并处理.的作用

    awk(关键字:分析&处理) 一行一行的分析处理 awk '条件类型1{动作1}条件类型2{动作2}' filename, awk 也可以读取来自前一个指令的 standard input相对 ...

  5. Dev-Cpp/Code::Block/MinGW下使用EasyX

    众所周知,EasyX是个很香的东西,但EasyX目前只支持Visual Studio,那么如果要在MinGW(Dev-Cpp和Code::Block均使用这个编译器)上使用EasyX怎么办呢? 这篇文 ...

  6. IDEA格式化代码快捷键失灵原因

    网易云音乐快捷键与IDEA快捷键冲突了!!!!!!!坑爹

  7. es6模块化设计

    //导出 //方式一 export const name = 'hello' export let addr = 'chengdu' export var list = [1,2,3] //方式二 c ...

  8. MyBatis(2)——增删改查

    增删改查: 1.在实体类的映射文件中增加insert.update.delete标签与数据库语句,例如 <!-- 会去获取到对应的实体类的getter方法 --> <insert i ...

  9. Spring AOP编程(二)-AOP实现的三种方式

    AOP的实现有三种方式: l         aop底层将采用代理机制进行实现. l         接口 + 实现类 :spring采用 jdk 的动态代理Proxy. l         实现类: ...

  10. TCP和UDP的一些注意事项

    TCP的一些注意事项 1. tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器,更无法链接到服务器 2. tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip.port等 ...