单例模式总结-java版

0.更新日志

  • 2019-4-1 第一次完成此文章V1.0.0版本
  • 2019-4-10 更新标题,增加了“java版”字样,文章版本更新至V1.0.1

1.单例模式概述

如何保证一个类只有一个实例并且这个实例易于被访问?
(1) 全局变量:可以确保对象随时都可以被访问,但不能防止创建多个对象
(2) 让类自身负责创建和保存它的唯一实例,并保证不能创建其他实例,它还提供一个访问该实例的方法。

2.单例模式的定义

单例模式:确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。

要点:

  • 某个类只能有一个实例
  • 必须自行创建这个实例
  • 必须自行向整个系统提供这个实例

3.单例模式的两种java实现方式(饿汉式与懒汉式)

饿汉式写法

public class Singleton {
	private static final Singleton instance=new Singleton();
	private Singleton() {}//为避免被其他类创建实例,该构造函数设为私有
	public static Singleton getInstance() {
		return instance;
	}
	//定义一个show()方法表示一下成功创建
	public void show() {
		System.out.println("饿汉式单例模式已运行!");
	}
}

测试一下

public class Main {
	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance();
		singleton.show();
	}
}

懒汉式写法

public class Singleton {
	private static Singleton instance=null;
	private Singleton() {}//为避免被其他类创建实例,该构造函数设为私有
	synchronized public static Singleton getInstance() {
	    //synchronize是java中用于加锁的关键字
		if(instance==null) instance=new Singleton();
		return instance;
	}
	//定义一个show()方法表示一下成功创建
	public void show() {
		System.out.println("懒汉式单例模式已运行!");
	}
}

测试一下

public class Main {
	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance();
		singleton.show();
	}
}

4.饿汉式与懒汉式的优缺点

饿汉式优点

  1. 获取对象速度快,节省了运行时间。
  2. 结构清晰。
  3. 线程安全。

饿汉式缺点

  1. 类加载速度慢,效率低。如果是工厂模式,当缓存多个实例时,使用饿汉式,在类加载时会一次性全部创建实例。
  2. 不能延时加载。

懒汉式优点

  1. 节约内存空间,避免内存浪费。
  2. 类加载速度快。
  3. 可以延时加载。

懒汉式缺点

  1. 获取对象速度慢。
  2. 不加同步锁的懒汉式写法线程不安全。(即没有synchronized关键字)

两者的区别:

  1. 线程安全性。饿汉式线程安全,懒汉式线程不加同步锁不安全。
  2. 实现方式(最大区别)。懒汉式是延时加载,它是在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建。
  3. 建立单例对象的时间不同。懒汉式是在真正用到时才去建单例对象,饿汉式不管用不用得上,一开始就建立单例对象。

两者的比较

  • 饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长。
  • 懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响。

关于懒汉式线程安全问题

线程问题安全描述:
1.现有两个线程A,B同时执行懒汉式 getInstance() 方法,它们都运行到 if 判断处中断(停下)。
2.线程A恢复运行,执行到 instance= 时再次中断;线程B恢复运行,一直运行到函数结束。
3.线程A又恢复运行,一直运行到线程结束。

此时会出现问题:线程B已经创建了实例,但线程A又创建了一次!

5.怎么解决懒汉式线程安全问题

解决方法:加上同步锁 synchronized。写法如上文代码所示。

但是这样在多线程并发访问的情况下,每个线程每次获取实例都要判断下锁,效率比较低,为了提高效率,可以使用双重检查加锁方法(又称为双重式单例模式)。

public class Singleton {
	private volatile static Singleton instance = null;  
    private Singleton(){  //为避免被其他类创建实例,该构造函数设为私有
    }  
    public static  Singleton getInstance(){  
        //先检查实例是否存在,如果不存在才进入下面的同步块  
        if(instance == null){  
            //同步块,线程安全地创建实例  
            synchronized(Singleton.class){  
                //再次检查实例是否存在,如果不存在才真正地创建实例  
                if(instance == null){  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
	public void show() {
		System.out.println("懒汉式单例模式已运行!");
	}
}

6.单例模式优缺点以及适用环境

模式优点

  1. 提供了对唯一实例的受控访问
  2. 可以节约系统资源,提高系统的性能
  3. 允许可变数目的实例(多例类)。

模式缺点

  1. 扩展困难(缺少抽象层)。
  2. 单例类的职责过重
  3. 由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失

适用环境

  1. 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象
  2. 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

7.单例模式双登式写法

PS:在查资料的发现居然还有这个写法,所以记录一下。

public class Singleton {
	private Singleton() {}
    public static Singleton getInstance() {
        return Holder.instance;
    }
    private static class Holder {
       private static final Singleton instance = new Singleton();
    }
	public void show() {
		System.out.println("双登式单例模式已运行!");
	}
}

8.单例模式应用实例

请结合单例模式实现基于txt的配置文件的读取。
Txt文件可以自行建立,例如:
1 2003 Spring Soccer League (Spring '03)
2 2003 Summer Summer Soccer Fest 2003
3 2003 Autumn Autumn Soccer League (2003)
4 2004 Spring Soccer League (Spring '04)
5 2005 Summer The Summer of Soccer Love 2005
6 2006 Autumn Autumn Soccer League (2006)

目录结构如下
在这里插入图片描述

public class ReadTxt {//本实例采用懒汉式
	private static ReadTxt rt;
	private ReadTxt(){
		
	}
	public static synchronized ReadTxt getInstance() {
		if (rt==null) {
			rt = new ReadTxt();
		}
		return rt;
	}
	public void readTxt(String pathname) {//定义读文件的方法
		try {
			FileReader f = new FileReader(pathname);
			BufferedReader br = new BufferedReader(f);
			String s = br.readLine();
			while(s!=null) {
				System.out.println(s);
				s=br.readLine();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

测试一下

public class Main {
	public static void main(String[] args) {
		//通过类加载器来获得文件路径
		String pathname = Main.class.getResource("1.txt").getPath();
		ReadTxt rt = ReadTxt.getInstance();
		rt.readTxt(pathname);
	}
}

9.参考博客

博主 AH_HH:单例模式懒汉式和饿汉式区别
https://blog.csdn.net/qq_35098526/article/details/79893628
博主 dxdt1111:懒汉式单例和饿汉式单例优缺点
https://blog.csdn.net/u014284952/article/details/26138355
博主 liyue199512:单例模式(懒汉式、双重锁、饿汉式、登记式)
https://blog.csdn.net/liyue199512/article/details/52135659

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值