Java设计模式——单例模式

本文介绍了单例模式的概念、特点和应用场景,详细讲解了饿汉式和懒汉式两种单例模式的实现,以及它们在多线程环境下的问题和解决方案,包括synchronized同步方法、同步代码块、双重校验和静态内部类。此外,还提到了使用枚举类实现单例的线程安全性。
摘要由CSDN通过智能技术生成

一、单例模式概述

实现了单例模式(Singleton)的类在创建时,只能生成一个实例对象供外部访问,并且在全局只提供一个访问点。

1.单例模式的特点

  1. 单例类只有一个实例对象
  2. 单例类必须私有化构造器,这意味着其他对象无法直接通过new 关键字创建单例类的实例化对象
  3. 单例类必须在类内创建自身的实例对象,并私有化
  4. 单例类为其他对象提供公有静态方法返回自身的实例对象

2.应用场景

多用户同时访问数据库时的同一张表时,主键对应的对象只能有一个,否则会造成数据库插入数据时冲突

二、单例模式的分类

1.饿汉式单例模式(线程绝对安全)

/**
 * 饿汉式单例模式
 */
public class SingletonV1 {
    //构造器私有化
    private SingletonV1(){ }

    //创建单例私有静态对象实例,类加载时创建
    private static SingletonV1 singletonV1 = new SingletonV1();

    //通过静态公有方法为其他对象提供实例对象
    public SingletonV1 getInstance(){
        return singletonV1;
    }

}

由于对象实例在类创建时就加载完成,并且只有一个,不会存在多线程访问问题

2.懒汉式单例模式(存在多线程问题)

/**
 * 懒汉式单例模式
 */
public class SingletonV2 {
    //构造器私有化
    private SingletonV2(){ }

    //创建单例私有静态对象实例的引用,具体使用时再初始化
    private static SingletonV2 singletonV2 = null;

    //通过静态公有方法为其他对象提供实例对象
    public static SingletonV2 getInstance(){
        if (singletonV2 == null){
            singletonV2 = new SingletonV2();
        }
        return singletonV2;
    }
}
/**
 1. 懒汉式单例模式存在多线程问题
 */
class TestSingletonV2Thread implements Runnable{
    @Override
    public void run() {
        //获得懒汉式实例对象
        SingletonV2 singletonV2 = SingletonV2.getInstance();
        //输出实例对象的HashCode
        System.out.println(singletonV2.hashCode());
    }
}
public class SingletonV2Test {

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            Thread thread = new Thread(new TestSingletonV2Thread());
            thread.start();
        }
        /**
         * 结果:
         * 222746741
         * 240767393
         * 222746741
         * 222746741
         * 222746741
         * 222746741
         * 222746741
         * 222746741
         * 222746741
         * 222746741
         *
         * 主要是由于存在A线程在即将执行new SingletonV2()时,B线程进行if (singletonV2 == null)的判断
         * 然后又执行new SingletonV2(),导致实例对象不一致,违背单例模式的设计思想
         */
    }
}

3.懒汉式单例模式多线程环境下优化

  1. 使用synchronized同步方法
/**
 * 懒汉式单例模式使用synchronized同步方法加锁
 */
public class SingletonV2 {
    //构造器私有化
    private SingletonV2(){ }

    //创建单例私有静态对象实例的引用,具体使用时再初始化
    private static SingletonV2 singletonV2 = null;

    /** 通过静态公有方法为其他对象提供实例对象
     *
     * 通过synchronized为getInstance()方法加锁,使得一次只能有一个线程执行该方法
     * 缺点:synchronized关键字修饰的作用域太大,锁粒度比较粗,执行的效率低
     */
    public static synchronized SingletonV2 getInstance(){
        if (singletonV2 == null){
            singletonV2 = new SingletonV2();
        }
        return singletonV2;
    }
}

2.使用synchronized同步代码块

/**
 * 懒汉式单例模式使用synchronized同步代码块加锁
 */
public class SingletonV2 {
    //构造器私有化
    private SingletonV2(){ }

    //创建单例私有静态对象实例的引用,具体使用时再初始化
    private static SingletonV2 singletonV2 = null;

    /** 通过静态公有方法为其他对象提供实例对象
     *
     * 通过synchronized代码块加锁,使得一次只能有一个线程执行该方法
     * synchronized代码块虽然比synchronized同步方法的作用域小,但差距不大
     */
    public static SingletonV2 getInstance(){
    	synchronized(SingletonV2.class){
        	if (singletonV2 == null){
            	singletonV2 = new SingletonV2();
        	}
        	return singletonV2;
        }
    }
}

3.使用双重校验(double check)

/**
 * 懒汉式单例模式使用双重校验
 */
public class SingletonV2 {
    //构造器私有化
    private SingletonV2(){ }

    //需要使用voltaile关键字防止指令重排序
    private static voltaile SingletonV2 singletonV2 = null;

    /** 通过静态公有方法为其他对象提供实例对象
     *
     * 先判断实例对象是否为空,再决定是否需要进入同步代码块
     */
    public static SingletonV2 getInstance(){
        if (singletonV2 == null) {
            synchronized (SingletonV2.class){
            	//同步代码块中再对实例对象判断是否为空
                if (singletonV2 == null) {
                    singletonV2 = new SingletonV2();
                }
            }
        }
        return singletonV2;
    }
}

4.使用静态内部类

/**
 * 懒汉式单例模式(使用静态内部类保证线程安全)
 */
public class SingletonV2 {
    //构造器私有化
    private SingletonV2(){ }

    //创建私有静态内部类
    private static class InnerClass{
    	private static SingletonV2 singletonV2 = new SingletonV2;
    }

    //通过静态公有方法为其他对象提供实例对象
    public static SingletonV2 getInstance(){
    	//调用内部类的属性
        retrun InnerClass.singletonV2;
    }
}

5.使用枚举类
由于枚举类是线程安全的,并且只会加载一次,所以利用这个特性,可以通过枚举类来实现单例。

/**
 * 懒汉式单例模式(使用枚举类保证线程安全)
 */
public class SingletonV2 {

    //构造器私有化
    private SingletonV2(){
    
    }
    //定义一个枚举类
    private enum SingletonEnum{
        //定义一个枚举实例对象
        Instance;

        private SingletonV2 singletonV2;

        //在枚举类的构造方法内实例化单例类
        SingletonEnum(){
            singletonV2 = new SingletonV2();
        }
        
        private SingletonV2 getInstance(){
            return singletonV2;
        }
    }
    
    //获得单例类的实例对象
    public static SingletonV2 getInstance(){
        return SingletonEnum.Instance.getInstance();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值