Java设计模式-单例模式(五)

目录

一、什么是单例模式

二、示例程序

三、UML图

四、拓展思路与项目实战

1、为什么必须设置限制

2、何时生成这个唯一的实例

3、如何确保只生成一个实例

4、如何生成最多三个Triple类的实例

5、关于线程安全的探讨


一、什么是单例模式

确保在程序中只生成一个实例的模式我们称之为单例模式(Singleton)。

二、示例程序

1、Singleton类

package com.as.module.Singeton;

/**
 * 单例类
 * @author Andy
 * @date 2021/4/24 21:04
 */
public class Singleton {
    public static Singleton singleton = new Singleton();
    private Singleton(){
        System.out.println("生成了一个实例");
    }

    public static Singleton getInstance(){
        return singleton;
    }


}

2、测试类

package com.as.module.Singeton;

/**
 * TODO
 *
 * @author Andy
 * @date 2021/4/24 21:06
 */
public class TestSingleton {
    public static void main(String[] args) {
        System.out.println("Start");
        Singleton obj1 = Singleton.getInstance();
        Singleton obj2 = Singleton.getInstance();
        if(obj1==obj2){
            System.out.println("obj1和obj2是相同的");
        }else{
            System.out.println("obj1和obj2是不同的");
        }
        System.out.println("end.");
    }
}

3、运行结果

三、UML图

单例模式中的登场角色:

1、Singleton

在Singleton模式中,只有Singleton这一个角色。Singleton角色中有一个返回唯一实例的static方法。该方法总是会返回同一个实例。

 

四、拓展思路与项目实战

1、为什么必须设置限制

Singleton模式对实例的数量设置了限制。为什么要在程序中特意设置这个限制呢?设置限制其实就是为程序增加一项前提条件。

当存在多个实例时,实例之间相互影响,可能会产生意想不到的Bug。

但是,如果我们可以确保只有一个实例,就可以在这个前提条件下放心地编程了

2、何时生成这个唯一的实例

在程序运行后,第一次调用getInstance方法时,Singleton类会被初始化。也就是在这个时候,static字段singleton被初始化,生成了唯一的一个实例。

3、如何确保只生成一个实例

我们在Singleton模式中定义了用于获取唯一一个实例的static方法,同时,为了防止不小心使用new关键字创建实例,还将构造函数设置为private.

4、如何生成最多三个Triple类的实例

Triple类

package com.as.module.Singeton;

/**
 * 三实例
 * @author Andy
 * @date 2021/4/24 21:26
 */
public class Triple {
    private int id;

    public  static Triple[] triple = new Triple[]{
            new Triple(0),
            new Triple(2),
            new Triple(2)
    };


    private Triple(int id){
        System.out.println("The instance "+id+" is created");
    }

    public static Triple getInstance(int id){
        return triple[id];
    }

    @Override
    public String toString() {
        return "Triple{" +
                "id=" + id +
                '}';
    }
}

测试类:

package com.as.module.Singeton;

/**
 * TODO
 *
 * @author Andy
 * @date 2021/4/24 21:06
 */
public class TestSingleton {
    public static void main(String[] args) {
//        System.out.println("Start");
//        Singleton obj1 = Singleton.getInstance();
//        Singleton obj2 = Singleton.getInstance();
//        if(obj1==obj2){
//            System.out.println("obj1和obj2是相同的");
//        }else{
//            System.out.println("obj1和obj2是不同的");
//        }
//        System.out.println("end.");


        System.out.println("Start");
        for(int i=0;i<9;i++){
            Triple triple = Triple.getInstance(i%3);
            System.out.println(i+":"+i);
        }
        System.out.println("end.");
    }
}

测试运行结果:

 

在上述代码中,triple字段并不是实例的字段,而是静态字段。triple字段的初始化只会在第一次生成时进行,之后生成Triple类的实例时不会再初始化triple字段。如果不将triple字段定义为静态字段,就会进入无限循环,在运行时会报错。

5、关于线程安全的探讨

示例代码如下(也称为懒汉式单例):

package com.as.module.Singeton;

/**
 * 懒汉式单例
 * @author Andy
 * @date 2021/4/24 21:54
 */
public class SluggardSingleton {
    private static SluggardSingleton sluggardSingleton = null;
    private SluggardSingleton(){
        System.out.println("生成了一个实例");
        //确保能生成多个实例,线程睡1秒中
        slowdown();
    }
    public static SluggardSingleton getInstance(){
        if(sluggardSingleton==null){
            sluggardSingleton = new SluggardSingleton();
        }
        return sluggardSingleton;
    }

    private void slowdown(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

其中,为了确保能出现生成多实例现象,故意降低了程序处理速度

测试类:

package com.as.module.Singeton;

/**
 * TODO
 *
 * @author Andy
 * @date 2021/4/24 21:06
 */
public class TestSluggardSingleton  extends Thread{
    public static void main(String[] args) {
        System.out.println("Start.");
        new TestSluggardSingleton("A").start();
        new TestSluggardSingleton("B").start();
        new TestSluggardSingleton("C").start();
    }

    public void run(){
        SluggardSingleton obj = SluggardSingleton.getInstance();
        System.out.println(getName()+":obj="+obj);
    }

    public TestSluggardSingleton(String name){
        super(name);
    }
}

运行结果:

由执行结果可见,如下条件判断不是线程安全的:

在使用sluggardSingleton==null判断第一个实例是否为null后,执行了下面的语句。

sluggardSingleton = new SluggardSingleton();

但是,在赋值之前,其他线程可能会进行sluggardSingleton==null判断。

因此,只有将getInstance()方法定义为synchronized方法后才是严谨的Singleton模式

再次执行测试,基于懒汉式的单例模式全局只有一个实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值