一、概念
单例模式是Java中最简单的设计模式之一,用于确保一个类只能创建一个对象实例,而且自行实例化并提供了一个全局访问点来获取该实例。
在单例模式中主要分为两类,分别是懒汉式和饿汉式:
1.懒汉式:在程序调用时才创建实例
2.饿汉式:在程序加载时就创建好实例,等待被调用
通常,单例模式的实现中包含以下三个关键元素:
- 私有的构造函数:将类的构造函数设置为私有,以防止从外部直接实例化对象。
- 静态成员变量:在类中定义一个静态成员变量,用于存储唯一的实例对象。
- 静态方法或函数:提供一个静态方法(通常命名为
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,即为同一个对象
}
}