Java设计模式——单例模式

文章介绍了Java设计模式中的单例模式,包括饿汉式和懒汉式的实现方式。饿汉式在类加载时创建实例,保证线程安全但可能造成内存浪费;懒汉式在首次使用时创建实例,但在多线程环境下可能不安全。为解决懒汉式的线程安全问题,文章提到了双重检查锁模式,通过添加volatile关键字来保证可见性和有序性,实现线程安全的单例模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概念

     单例模式是Java中最简单的设计模式之一,用于确保一个类只能创建一个对象实例,而且自行实例化并提供了一个全局访问点来获取该实例。

     在单例模式中主要分为两类,分别是懒汉式和饿汉式:

                1.懒汉式:在程序调用时才创建实例
                2.饿汉式:在程序加载时就创建好实例,等待被调用

通常,单例模式的实现中包含以下三个关键元素:

  1. 私有的构造函数:将类的构造函数设置为私有,以防止从外部直接实例化对象。
  2. 静态成员变量:在类中定义一个静态成员变量,用于存储唯一的实例对象。
  3. 静态方法或函数:提供一个静态方法(通常命名为getInstance),用于获取类的唯一实例。该方法会检查静态成员变量,如果不存在实例,则创建一个新的实例并存储在静态成员变量中,然后返回该实例。

二、单例模式的实现 

 1、饿汉式

 在类加载的时候就进行了实例化,会一直在内存中。存在问题:如果一直不去用,该对象仍在,会存在内存浪费问题。

class HungryPerson{
	//一开始就在该类中创建一个该类的对象供外界去使用
	private static HungryPerson  hungryPerson=new HungryPerson();
	
	private HungryPerson (){}//私有构造方法,为了不让外界创建该类的对象
	//提供一个公共的访问方式,让外界获取hungryPerson对象
	public static HungryPerson getInstance(){
		return hungryPerson;
	}
	
}
public class Test8 {
    public static void main(String[] args) {
    	//获取单例类的对象,因为对象私有,只能通过方法去获取
    	HungryPerson instance= HungryPerson.getInstance();
    	//为了验证单例类的对象是否一直存在且为同一个对象,再获取一次对象。
    	HungryPerson instance1= HungryPerson.getInstance();
    	System.out.println(instance.equals(instance1));//输出true,即为同一个对象
    	
    }
}
2、懒汉式

在类加载的时候不进行实例化,在第一次使用的时候再进行实例化。存在问题:线程安全问题

2.1、线程不安全的情况下:
class HungryPerson{
	//一开始就在该类中创建一个该类的对象供外界去使用
	private static HungryPerson  hungryPerson;
	
	private HungryPerson (){}//私有构造方法,为了不让外界创建该类的对象
	//提供一个公共的访问方式,让外界获取hungryPerson对象
	public static HungryPerson getInstance(){
		//判断hungryPerson是否为null,如果为null,说明还没有创建HungryPerson类的对象
        //如果没有,创建一个并返回;如果有,直接返回
        //线程不安全,多线程下会创建多个对象
		if(hungryPerson == null) {
			hungryPerson = new HungryPerson();
		}
		return hungryPerson;
	}
}
public class Test8 {
    public static void main(String[] args) {
    	//获取单例类的对象,因为对象私有,只能通过方法去获取
    	HungryPerson instance= HungryPerson.getInstance();
    	//为了验证单例类的对象是否一直存在且为同一个对象,再获取一次对象。
    	HungryPerson instance1= HungryPerson.getInstance();
    	System.out.println(instance.equals(instance1));//输出true,即为同一个对象
    	
    }
}
2.2、双重检查锁模式

    为了解决懒汉式线程不安全问题,需要加锁,来防止它多次被实例化。双重检查锁模式解决了单例、性能、线程安全问题。但也有存在问题,在多线程情况下,可能会出现空指针问题,问题在于JVM在实例化对象时会进行优化和指令重排序操作。解决空指针问题只需使用volatile关键字,volatile可以保证可见性和有序性。因此使用双重检查锁进行初始化的实例必须使用Volatile关键字修饰。

class HungryPerson{
	//一开始就在该类中创建一个该类的对象供外界去使用
	private volatile static HungryPerson  hungryPerson;
	
	private HungryPerson (){}//私有构造方法,为了不让外界创建该类的对象
	//提供一个公共的访问方式,让外界获取hungryPerson对象
	public static HungryPerson getInstance(){
		//第一次判断,如果hungryPerson不为null,不需要抢占锁,直接返回对象
		if(hungryPerson == null) {
			synchronized (HungryPerson.class) {
				//第二次判断hungryPerson是否为null,如果为null,说明还没有创建HungryPerson类的对象
				if(hungryPerson==null) {
					hungryPerson =new HungryPerson();
				}
			}
		}
		return hungryPerson;
	}
}
public class Test8 {
    public static void main(String[] args) {
    	//获取单例类的对象,因为对象私有,只能通过方法去获取
    	HungryPerson instance= HungryPerson.getInstance();
    	//为了验证单例类的对象是否一直存在且为同一个对象,再获取一次对象。
    	HungryPerson instance1= HungryPerson.getInstance();
    	System.out.println(instance.equals(instance1));//输出true,即为同一个对象
    	
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值