Java设计模式--单例模式

14 篇文章 0 订阅
4 篇文章 0 订阅
本文详细介绍了Java中的单例模式,包括其定义、要素和两种常见实现方式:饿汉式和懒汉式。进一步讨论了线程安全问题,提出了同步锁、双重检查机制和静态内部类等解决方案。最后提到了反射可能破坏单例模式的情况,展示了如何通过反射创建额外的实例。
摘要由CSDN通过智能技术生成

一、单例模式简介

单例模式是Java中最简单的设计模式之一。这种设计模式属于创建型模式,它提供了一种创建对象的最佳方式。单例模式类负责创建自己的对象,同时确保只有一个对象被创建,提供了一个访问其唯一对象的方式,可以直接访问,不需要实例化这个对象。

二、单例模式的要素

  1. 单例模式类只能有一个实例,需要私有其构造方法,确保只有自己能创建实例。
  2. 单例模式类需要给其他对象提供一个访问其实例的全局方法。

三、单例模式的简单实现方式

1.饿汉式

  • 饿汉式是在单例类初始化加载时就创建一个单例对象供外部使用。但这个方式会造成资源浪费,因为不管是否使用,一开始就将其对象创建。
  • 实例:
package com.lee.单例模式;

public class SingleDemo_01 {
    private SingleDemo_01(){}
    private static SingleDemo_01 singleDemo_01 = new SingleDemo_01();

    public static SingleDemo_01 getSingleDemo_01() {
        return singleDemo_01;
    }

    public static void main(String[] args) {
        System.out.println(SingleDemo_01.getSingleDemo_01().getClass().getName());
    }
}

2.懒汉式

  • 懒汉式也称懒加载式,它在第一次调用对象时创建,避免了不使用时资源浪费。
  • 实例:
package com.lee.单例模式;

public class SingleDemo_02 {
    private SingleDemo_02(){}
    private static SingleDemo_02 singleDemo_02;

    public static SingleDemo_02 getSingleDemo_02() {
        if (singleDemo_02 == null){
            singleDemo_02 = new SingleDemo_02();
        }
        return singleDemo_02;
    }

    public static void main(String[] args) {
        System.out.println(SingleDemo_02.getSingleDemo_02().getClass().getName());
    }
}

四、线程安全的懒汉式单例模式

在上述的懒汉式中,因为使用的是延迟加载方式,所以可能会产生多个单例对象。例如:有A和B线程都在执行,当A线程执行到判断对象是否为null时被切换出去让出CPU的使用权给B,这个时候线程B就会直接从创建对象开始执行,这就可能会导致多个单例对象被创建。

1.同步锁方式
懒汉模式在获取方法判空时会出现多线程安全问题,所以我们可以给这段代码加上一个同步锁,这样就能在同一时刻只有一个线程能判断单例对象是否为空,从而创建唯一单例对象。

package com.lee.单例模式;

public class SingleDemo_02 {
    private SingleDemo_02(){}
    private static SingleDemo_02 singleDemo_02;

    public static SingleDemo_02 getSingleDemo_02() {
        synchronized (SingleDemo_02.class){ //加入同步锁
            if (singleDemo_02 == null){
                singleDemo_02 = new SingleDemo_02();
            }
        }
        return singleDemo_02;
    }

    public static void main(String[] args) {
        System.out.println(SingleDemo_02.getSingleDemo_02().getClass().getName());
    }
}

2.双重检测机制
在方法上加synchronized同步锁或使用同步代码块对类加同步锁,此这种方式虽然解决了多个实例对象问题,但是运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。在此基础进行改进,得到双重检测机制,实现如下:

package com.lee.单例模式;

public class SingleDemo_02 {
    private SingleDemo_02(){}
    private static SingleDemo_02 singleDemo_02;

    public static SingleDemo_02 getSingleDemo_02() {
        if (singleDemo_02 == null){
            synchronized (SingleDemo_02.class){ //加入同步锁
                if (singleDemo_02 == null){
                    singleDemo_02 = new SingleDemo_02();
                }
            }
        }
        return singleDemo_02;
    }

    public static void main(String[] args) {
        System.out.println(SingleDemo_02.getSingleDemo_02().getClass().getName());
    }
}
  • 通过这样的方法,首先判断单例对象是否为空,如果不为空则直接返回单例对象,如果为空则将创建单例对象的代码定义为同步块,防止创建多个单例对象,这样做可以大大减小性能开销。

3.加强版双重检测机制
然而双重检测机制由于会有指令重排的发生,可能导致创建的单例对象为null的指针,因为创建对象的操作并非一个原子性操作,因此我们可以使用Java提供的volatile来防止指令重排。

package com.lee.单例模式;

public class SingleDemo_02 {
    private SingleDemo_02(){}
    private static volatile SingleDemo_02 singleDemo_02;

    public static SingleDemo_02 getSingleDemo_02() {
        if (singleDemo_02 == null){
            synchronized (SingleDemo_02.class){ //加入同步锁
                if (singleDemo_02 == null){
                    singleDemo_02 = new SingleDemo_02();
                }
            }
        }
        return singleDemo_02;
    }

    public static void main(String[] args) {
        System.out.println(SingleDemo_02.getSingleDemo_02().getClass().getName());
    }
}

4.静态类部类实现
上述方式,要么是加载类时直接创建单例对象,没有线程安全的隐患,但是在不使用单例时浪费了资源;要么是延迟创建单例对象,直到需要时才创建,但是存在线程安全的隐患。因此,考虑有没有一种方式,能够同时兼顾两种方式的优点。既能在第一次使用时被创建,又能避免使用同步锁,保证高并发环境下的性能。那就是静态类部类

package com.lee.单例模式;

public class SingleDemo_Static {
    private static class Single_Static{
        private static SingleDemo_Static singleDemo_static = new SingleDemo_Static();
    }
    public static SingleDemo_Static getInstance(){
        return Single_Static.singleDemo_static;
    }

    public static void main(String[] args) {
        System.out.println(getInstance().getClass().getName());
    }
}

五、反射破坏单例模式

package com.lee.单例模式;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class SingleDemo_Static {
    private static class Single_Static{
        private static SingleDemo_Static singleDemo_static = new SingleDemo_Static();
    }
    public static SingleDemo_Static getInstance(){
        return Single_Static.singleDemo_static;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        SingleDemo_Static s1 = SingleDemo_Static.getInstance();
        SingleDemo_Static s2 = SingleDemo_Static.getInstance();
        Constructor<SingleDemo_Static> constructor = SingleDemo_Static.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        SingleDemo_Static s3 = constructor.newInstance();
        System.out.println("对象s1和s2是否相同?"+(s1==s2));
        System.out.println("对象s1和s3是否相同?"+(s1==s3));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值