文章目录
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.饿汉式与懒汉式的优缺点
饿汉式优点
- 获取对象速度快,节省了运行时间。
- 结构清晰。
- 线程安全。
饿汉式缺点
- 类加载速度慢,效率低。如果是工厂模式,当缓存多个实例时,使用饿汉式,在类加载时会一次性全部创建实例。
- 不能延时加载。
懒汉式优点
- 节约内存空间,避免内存浪费。
- 类加载速度快。
- 可以延时加载。
懒汉式缺点
- 获取对象速度慢。
- 不加同步锁的懒汉式写法线程不安全。(即没有synchronized关键字)
两者的区别:
- 线程安全性。饿汉式线程安全,懒汉式线程不加同步锁不安全。
- 实现方式(最大区别)。懒汉式是延时加载,它是在需要的时候才创建对象,而饿汉式在虚拟机启动的时候就会创建。
- 建立单例对象的时间不同。懒汉式是在真正用到时才去建单例对象,饿汉式不管用不用得上,一开始就建立单例对象。
两者的比较
- 饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长。
- 懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响。
关于懒汉式线程安全问题
线程问题安全描述:
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.单例模式优缺点以及适用环境
模式优点
- 提供了对唯一实例的受控访问。
- 可以节约系统资源,提高系统的性能。
- 允许可变数目的实例(多例类)。
模式缺点
- 扩展困难(缺少抽象层)。
- 单例类的职责过重。
- 由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失。
适用环境
- 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象。
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。
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