什么是单例模式?

单例模式是一种常见的设计模式,单例模式的写法有很多种,这里主要介绍三种: 懒汉式单例模式、饿汉式单例模式、登记式单例 。

单例模式有以下特点:

1、单例类只能有一个实例。

2、单例类必须自己创建自己唯一的实例。

3、单例类必须给所有其它对象提供这一实例。

单例模式确保某各类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能,每台计算机可以有若干个打印机,但只能有一个Printer spooler,以避免两个打印作业同时输出到打印机中,每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态.

在将单例之前,要做一次基础知识的科普行动,大家都知道Java类加载器加载内容的顺序:

1、从上往下(Java的变量需要声明才能使用)

2、先静态后动态(对象实例化) (静态块和static关键字修饰在实例化以前分配内存空间)

3、先属性后方法(成员变量不能定义在方法中,只能定义在class下)

懒汉式单例(4种写法)

懒汉式顾名思义:需要用到的时候才会初始化

饿汉式:不管用不用先实例化

注册登记式:相当于有一个容器装载实例,在实例产生之前会先检查一下容器看有没有,如果有就直接取出来使用,如果没有就先new一个放进去,然后在后面的人使用,Spring IOC就是一种典型的注册登记单例

第一种写法:

/**

* Created by xingyuchao on 2018/1/20.

* 懒汉式单例类,在第一次使用的时候实例化自己

*/

public class Singleton {

//1.第一步先将构造方法私有化

private Singleton(){}

//2.然后声明一个静态变量保存单例的引用

private static Singleton single = null;

//3.通过提供一个静态方法来获得单例的引用

public static Singleton getInstance(){

if(single == null){

single = new Singleton();

}

return single;

}

}

Singleton1通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Signleton1的唯一实例只能通过getInstance()方法访问。

事实上,通过Java反射机制是能否实现实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效,此问题在此处不做讨论

但是以上懒汉式单例的实现没有考虑线程安全问题,它是非线程安全的,并发环境下可能出现多个Singleton1实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法的改造,保证了懒汉式单例的线程安全.

第二种写法:在getInstance()方法上加同步

/**

* Created by xingyuchao on 2018/1/29.

* 懒汉式单例类,保证线程安全

*/

public class Singleton2 {

//1.第一步先将构造方法私有化

private Singleton2(){}

//2.然后声明一个静态变量保存单例的引用

private static Singleton2 single = null;

//3.通过提供一个静态方法来获得单例的引用

// 为了保证线程环境下正确访问,给方法上加上同步锁synchronized

public static synchronized Singleton2 getInstance(){

if(single == null){

single = new Singleton2();

}

return single;

}

}

第三种写法:双重检测机制

/**

* Created by xingyuchao on 2018/1/29.

* 懒汉式单例类,保证线程安全 双重检测机制

*/

public class Singleton3 {

//1.第一步先将构造方法私有化

private Singleton3(){}

//2.然后声明一个静态变量保存单例的引用

private static Singleton3 single = null;

//3.通过提供一个静态方法来获得单例的引用

// 为了保证线程环境下的另一种实现方式,双重锁检查

public static synchronized Singleton3 getInstance(){

if(single == null){

single = new Singleton3();

}

return single;

}

}

第四种:静态内部类

/**

* Created by xingyuchao on 2018/1/29.

* 懒汉式单例类,通过静态内部类实现

*/

public class Singleton4 {

//1. 先声明一个静态内部类

//内部类的初始化,需要依赖主类

//也就是说,当JVM加载Singleton4类的时候LazyHolder类也会被加载

//只是目前还没有被实例化,需要等主类先实例化后,内部类才开始实例化

private static class LazyHolder{

//final是为了防止内部类将这个属性值覆盖掉

private static final Singleton4 INSTANCE = new Singleton4();

}

//2. 将默认构造方法私有化

private Singleton4(){}

//3.同样提供静态方法获取实例

//当getInstance方法第一次被调用的时候,它第一次读取LazyHolder.INSTANCE,内部类LazyHolder类得到初始化;

// 而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton4的实例,由于是静态的域,因此只会在

// 虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。这个模式的优势在于,getInstance方法并没有被同步,

// 并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。

//此处加final是为了防止子类重写父类方法

public static final Singleton4 getInstance(){

return LazyHolder.INSTANCE;

}

}

饿汉式单例(1种写法)

/**

* Created by xingyuchao on 2018/1/29.

* 饿汉式单例类,在类初始化时,已经自行初始化,不会产生线程安全问题

*/

public class Singleton5 {

//1.同样也是将默认构造方法私有化

private Singleton5(){}

//2.声明静态变量,在类初始化之前就初始化变量,将对象引用保存

//相反的如果这个单例对象一直没使用,那么内存空间也就被浪费掉了

private static final Singleton5 singleton = new Singleton5();

//3.开放静态方法,获取实例

public static Singleton5 getSingleton(){

return singleton;

}

}

枚举式单例(1种写法)

public class DBConnection {}

/**

* Created by xingyuchao on 2018/1/29.

* 枚举式单例

*/

public enum Singleton6 {

DATASOURCE;

private DBConnection connection = null;

private Singleton6() {

connection = new DBConnection();

}

public DBConnection getConnection() {

return connection;

}

}

/**

* Created by xingyuchao on 2018/1/29.

*/

public class Main {

public static void main(String[] args) {

DBConnection dbConnection1 = Singleton6.DATASOURCE.getConnection();

DBConnection dbConnection2 = Singleton6.DATASOURCE.getConnection();

System.out.println(dbConnection1 == dbConnection2); //true true结果表明两次获取返回了相同的实例。

}

}

这种方式不仅能避免多线程同步问题,而且能防止反射创建新的对象,可谓是很坚强的壁垒不过这种方式用的极少

为什么枚举会满足线程安全、序列化等标准。参考:http://blog..net/gavin_dyson/article/details/70832185

登记注册式单例

/**

* Created by xingyuchao on 2018/1/29.

* 登记式单例:类似spring里面的方法,将类名注册,下次直接从里面获取

*

* 登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记簿)中,对于已经登记过的实例,则从Map直接获取,对于没有登记的,则先登记,然后返回

*

* 内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在被装载的时候就被实例化了

*/

public class Singleton7 {

private static Map map = new HashMap<>();

static{

Singleton7 singleton7 = new Singleton7();

map.put(singleton7.getClass().getName(),singleton7);

}

//保护的默认构造

protected Singleton7(){}

//静态工程方法,返回此类的唯一实例

public static Singleton7 getInstance(String name) throws Exception {

if(name == null){

name = Singleton7.class.getName();

}

if(map.get(name) == null){

map.put(name,(Singleton7)Class.forName(name).newInstance());

}

return map.get(name);

}

}

测试:

public class Test {

public static void main(String[] args) throws Exception{

//启动100个线程同时去抢占cpu ,有可能产生并发,观察并发情况下是否为同一个对象实例

int count = 100;

//发令枪

CountDownLatch latch = new CountDownLatch(count);

for (int i = 0; i < count; i++){

//Lambda简化后

new Thread(()->{

System.out.println(System.currentTimeMillis() + ":" + Singleton4.getInstance());

}).start();

latch.countDown();

}

latch.await(); //开始法令,抢占cpu

}

}

结果:

分布式环境下的单例

有两个问题需要注意:

1. 如果单例类由不同的类装载器装载,那边可能存在多个单例类的实例。假定不是远端存取,例如有一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就会有各自的实例

解决:指定classloader

private static Class getClass(String classname) throws ClassNotFoundException{

ClassLoader classloader = Thread.currentThread().getContextClassLoader();

if(classloader == null){

classloader = Singleton.class.getClassLoader();

}

return (classloader.loadClass(classname));

}

2. 如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎么样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那么就会有多个类的实例

public class Singleton implements Serializable {

public static Singleton singleton = new Singleton();

protected Singleton(){}

private Object readResolve(){

return singleton;

}

}

java单例模式的七种写法_Java设计模式之单例模式的七种写法相关推荐

  1. filter java 是单例的吗_JAVA 设计模式之 单例模式详解

    单例模式:(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点.单例模式是创建型模式.单例模式在现实生活中应用也非常广泛. 在 J2EE 标准中,S ...

  2. java单例设计模式_Java设计模式之单例模式详解

    在Java开发过程中,很多场景下都会碰到或要用到单例模式,在设计模式里也是经常作为指导学习的热门模式之一,相信每位开发同事都用到过.我们总是沿着前辈的足迹去做设定好的思路,往往没去探究为何这么做,所以 ...

  3. java connection 单例_Java设计模式之单例模式详解

    Java设计模式之单例模式详解 什么是设计模式 设计模式是在大量的实践中总结和理论之后优选的代码结构,编程风格,以及解决问题的思考方式.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可 ...

  4. java+单例+恶汉_Java设计模式之单例模式(恶汉式和懒汉式)

    /** 单例模式:* 饿汉式:类一加载就创建对象* 懒汉式:用的时候,才去创建对象* 面试题:单例模式的思想是什么?写一个代码体现(我们最好写懒汉式的单例模式给面 /* * 单例模式: *       ...

  5. java与模式pdf 闫宏_Java设计模式及实践.pdf下载

    Java设计模式及实践.pdf下载 资料简介:本书向读者展示Java语言中更加智能化的编码实例.书中首先介绍面向对象编程(OOP)和函数式编程(FP)范式,然后描述常用设计模式的经典使用方法,并解释如 ...

  6. 3种设计模式java小程序_Java设计模式之单例模式(3种实现方式)

    饿汉模式 public class Singleton {//实例化private static Singleton instance=new Singleton();private Singleto ...

  7. java设计模式在线视频_Java设计模式之单例模式视频课程

    一.概述 多例模式(Multiton  Pattern),这种设计模式也属于对象创建型模式,本质上就是单例模式的推广 定义: 一个类有多个实例且由类本身创建和管理自己的实例,并向外部提供访问点. 二. ...

  8. java单例模式实例_Java设计模式之单例模式 通俗易懂 超详细 【内含案例】

    单例模式 什么是单例模式 ? 确保程序中一个类只能被实例化一次,实现这种功能就叫单例模式 单例模式的好处是什么 ? 方便控制对象 节省资源减少浪费 怎么实现单例模式 ? 构造私有化 调用静态方法返回实 ...

  9. java 循环写法_java的for循环的几种写法

    J2SE 1.5提供了另一种形式的for循环.借助这种形式的for循环,可以用更简单地方式来遍历数组和Collection等类型的对象.本文介绍使用这种循环的具体方式,说明如何自行定义能被这样遍历的类 ...

最新文章

  1. 股市币市:数据分析与交易所公告(20190226)
  2. 【每日一算法】合并两个有序数组
  3. linux redhat、ubuntu系统 docker启动、停止命令
  4. VS2015编译Poco+openssl,使用Poco发送HTTPS请求
  5. SAP 采购流程和销售流程
  6. 电商购物APP UI 模板素材,充满时尚感的设计
  7. server sql 多表事物 自增id_最实用的 SQL 语句收藏,程序员看完这篇就够了!
  8. 沉寂了多年金价为何会连续上涨 专家指出真正原因
  9. iOS-UIScrollView以及代理
  10. 蓝桥杯题解 时间显示 Java答案
  11. Tool-杂项-建模:犀牛(3D造型软件)
  12. 3D打印机DIY之一------Prusa i3的材料清单和总体结构组装
  13. 时间管理技巧(清理一波文件 感觉这个还是有用的 )
  14. python turtle绕原点旋转_每天一个Python小技巧,用Python 画个多啦A梦,小猪佩奇,文末还有Python入门学习视频
  15. linssh2 sftp读取远端目录,获取远端文件或者目录信息
  16. 神气的Android Studio -Pligins什么都没有
  17. 计算机二级11成绩查询时间,2020计算机二级考试成绩查询时间
  18. JAVA缴税_纳税服务系统【总结】
  19. YNB/酵母氮源基础 (含硫酸铵,不含氨基酸)的基本信息(供应SD/-Ade/-His/-Leu/-Met-Trp/-Ura with Agar/SD/-Cys/-Met/Ura with Agar)
  20. 大气温室气体浓度不断增加,导致气候变暖加剧,随之会引发一系列气象、生态和环境灾害怎样解决?

热门文章

  1. CodeForces 696B Puzzles
  2. 一个行外人看中国的电子竞技
  3. Android API 中文(77)——AdapterView.OnItemSelectedListener
  4. sql横着连接起来sql_SQL联接的简要介绍(到目前为止)
  5. 照顾好自己才能照顾好别人_您必须照顾的5个基本数据
  6. 递归函数基例和链条_链条和叉子
  7. isql 测试mysql连接_[libco] 协程库学习,测试连接 mysql
  8. p2020开发_2020年最佳开发者社区
  9. 成功的秘诀是什么_学习编码的10个成功秘诀
  10. 如何使用Apache的Prediction IO Machine Learning Server构建推荐引擎