赞
踩
在学习单例设计模式前,我想应该先去了解这种模式是在什么样的背景下产生的,这有利于后续学习时的理解。记得我自己最先真正使用到单例模式,是在进行数据库连接的时候,当时在做原生项目的时候,因为需要经常访问数据库,就会常常打开和关闭数据库连接,这种方式会导致项目的效率下降。在这种场景下,如果使用一个类,来对数据库资源的连接进行封装,保证在整个项目中,只有一个该类提供的对象,就可以避免上述的问题。所以,单例模式的作用也就体现出来了,当然它的应用场景还有许多,例如项目日志输出,统计网站在线人数等。
单例模式最初的定义出现于《设计模式》(艾迪生维斯理,1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”在Java中单例模式的定义是:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”
单例模式分为饿汉式和懒汉式两种,两者实现的思路相同:
1.在单例类中,提供一个私有静态属性,用于接收实例化对象;
2.在单例类中,构造方法必须私有化,防止在类外进行实例化;
3.在单例类中,向类外提供一个获取类对象的公有静态方法。
饿汉式单例模式:在系统加载类的时候就会创建类的对象,并保存在类中。它的特点是线程安全,以空间换时间。
public class Singleton { //1、私有静态属性 private static Singleton instance = new Singleton(); //2、私有构造方法 private Singleton() { System.out.println("***创建Singleton类对象***"); } //3、公有静态方法 public static Singleton getInstance() { return instance; } //测试 public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } //程序执行结果 ***创建Singleton类对象*** true
懒汉式单例模式:在系统加载类的时候不创建类的对象,直到第一次使用类的对象时才会进行创建。它的特点是线程不安全,以时间换空间。
public class Singleton { //1、私有静态属性 private static Singleton instance = null; //2、私有构造方法 private Singleton() { System.out.println("***创建Singleton类对象***"); } //3、公有静态方法 public static Singleton getInstance() { //判断类对象是否为空,若为空则创建类对象 if(instance == null){ instance = new Singleton(); } return instance; } //测试 public static void main(String[] args) { Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); } } //程序执行结果 ***创建Singleton类对象*** true
懒汉式单例模式是存在线程风险的,下面我们来测试一下,是不是像说的那样真的存在线程风险?
public class Singleton { //1、私有静态属性 private static Singleton instance = null; //2、私有构造方法 private Singleton() { System.out.println("【" + Thread.currentThread().getName() + "】***创建Singleton类对象***"); } //3、公有静态方法 public static Singleton getInstance() { //判断类对象是否为空,若为空则创建类对象 if(instance == null){ instance = new Singleton(); } return instance; } public void print() { System.out.println("**执行print()方法**"); } //测试 public static void main(String[] args) { //创建三个线程对象并运行 for(int i = 0; i < 3; i++){ new Thread(() -> { //在线程运行过程中,获取Singleton类对象 Singleton.getInstance().print(); }, "线程对象 - " + i).start(); } } } //程序执行结果 【线程对象 - 0】***创建Singleton类对象*** 【线程对象 - 1】***创建Singleton类对象*** **执行print()方法** 【线程对象 - 2】***创建Singleton类对象*** **执行print()方法** **执行print()方法**
通过程序的执行结果,发现当有了若干个线程对象,当前的程序就可以产生多个Singleton类的对象。显然,这不符合单例模式的特点,因为单例模式要求在程序的整体运行中只产生一个实例化对象。造成上述问题的关键是,代码本身出现了不同步的情况,而解决的核心就在于进行同步处理。
实现代码同步,我们需要使用到sychronized关键字。改进方法一,直接对公有静态方法做同步处理:
public static synchronized Singleton getInstance() {
//判断类对象是否为空,若为空则创建类对象
if(instance == null){
instance = new Singleton();
}
return instance;
}
//程序执行结果
【线程对象 - 0】***创建Singleton类对象***
**执行print()方法**
**执行print()方法**
**执行print()方法**
按照上述方法,我们的确实现了单例模式的要求,保证在程序整体运行过程中,单例类只提供一个实例化的对象。但是,使用这种方法做同步处理,效率会比较低,因为在整个方法中只有一个地方需要做同步处理,下面我们使用同步代码块进行更合理的同步处理。
public static Singleton getInstance() { //判断类对象是否为空,若为空则创建类对象 if(instance == null){ //同步代码块,静态方法中不允许使用this关键字,这里使用类的Class对象 synchronized(Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } //程序执行结果 【线程对象 - 0】***创建Singleton类对象*** **执行print()方法** **执行print()方法** **执行print()方法**
通过对“判断instance对象是否为空”作同步处理,这种方法可以减小同步产生的代价,使程序的执行效率更高一些。
懒汉式单例模式实现,使用这种方法也被称为DCL(双重检查加锁)。另外,为了在程序性能上再做一点提升,可以在单例模式的属性声明上使用volatile关键字,例如:
private static volatile Singleton instance = null;
到达文末了,十分感谢小伙伴们的阅读哈,如果您对我的文章有什么意见或者建议,也欢迎您来进行评论,私信我也可以哈!
代码如诗,小伙伴们和我一起努力加油,做持续学习者!:)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。