JavaSE面试题02:单例设计模式

简介: JavaSE面试题02:单例设计模式

单例设计模式

之前文章有提到过单例设计模式的初步介绍Java设计模式之单例设计模式

下文提到的枚举Java中的枚举类是什么?enum关键字怎么使用?

线程池Java多线程10—如何使用线程池创建线程?

涉及到的Java多线程09—实现Callable接口创建线程

下文懒汉式里面的同步方法解决线程问题Java多线程04—同步方法解决线程的安全问题


  • 什么是Singleton?

    • 在Java中指单例设计模式,它是软件开发中最常用的设计模式之一,单:唯一;例:实例
  • 单例设计模式:即某个类在整个系统中只能有一个实例对象可被获取和使用的代码模式
  • 例如:代表jvm运行环境的Runtime类

要点

一是某个类只能有一个实例

  • 构造器私有化

二是它必须自行创建这个实例

  • 含有一个该类的静态变量来保存这个唯一的实例

三是它必须自行向整个系统提供这个实例

  • 对外提供直接获取该实例对象的方式:

    • 直接暴露
    • 用静态变量的get方法获取

几种常见的方式

饿汉式

在类初始化时直接创建对象,不存在线程安全问题

直接实例化饿汉式(简洁直观)
/*
饿汉式:直接创建实例对象,不管是否需要这个对象,都会创建
(1):构造器私有化
(2):自行创建,并且用静态变量保存
(3):向外提供这个单例
(4):强调这是一个单例,我们可以用final修改
 */
public class Singleton01 {
    public static final Singleton01 INSTANCE = new Singleton01();
    private Singleton01() {
    }
}
枚举式(最简洁)
/*
枚举类型:表示该类型的对象是有限的几个,所以可以限定为一个,就成了单例
 */
public enum  Singleton02 {
    INSTANCE
}
静态代码块饿汉式(适合复杂实例化)
public class Singleton03 {
    public static final Singleton03 INSTANCE;
    static {
        INSTANCE=new Singleton03();
    }
    private Singleton03(){
    }
}
测试
/*
枚举类型打印出来的数据不一样,因为枚举是重写了toString方法,打印出来的是对象的名字
 */
public class TestSingleton {
    public static void main(String[] args) {
        Singleton01 instance1 = Singleton01.INSTANCE;
        System.out.println(instance1);//SingletonTest.Singleton01@6d6f6e28
        
        Singleton02 instance2 = Singleton02.INSTANCE;
        System.out.println(instance2);//INSTANCE
    }
}

懒汉式

延迟创建对象

线程不安全(适用于单线程)
/*
懒汉式:
延迟创建这个实例对象
1.构造器私有化
2.用一个静态变量保存这个唯一的实例
3.提供一个静态方法,获取这个实例对象
 */
public class Singleton04 {
    private static Singleton04 instance;
    private Singleton04(){
    }
    public static Singleton04 getInstance(){
        if(instance==null){
            instance=new Singleton04();
        }
        return instance;
    }
}

单线情况下测试:

public class TestSingleton02 {
    public static void main(String[] args) {
        //单线情况下
        Singleton04 singleton01=Singleton04.getInstance();
        Singleton04 singleton02=Singleton04.getInstance();
        //测试拿到的是不是同一个对象
        System.out.println(singleton01==singleton02);//true
    }
}
线程安全(适用于多线程)
  1. 可能不安全
import java.util.concurrent.*;

public class TestSingleton02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //单线情况下
//        Singleton04 singleton01=Singleton04.getInstance();
//        Singleton04 singleton02=Singleton04.getInstance();
//        System.out.println(singleton01==singleton02);//true
        //线程安全问题
        Callable<Singleton04> c = new Callable<Singleton04>(){
            @Override
            public Singleton04 call() throws Exception {
                return Singleton04.getInstance();
            }
        };

        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Singleton04> f1 = es.submit(c);
        Future<Singleton04> f2 = es.submit(c);

        Singleton04 s1 = f1.get();
        Singleton04 s2 = f2.get();

        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s2==s1);
        //运行结果有概率问题,可能false可能true
        es.shutdown();
       
    }
}

解决办法:同步方法synchronized

类Singleton05

//学习内容:
//开发时间:10月30日  21:38
package SingletonTest02;
/*
懒汉式:
延迟创建这个实例对象
1.构造器私有化
2.用一个静态变量保存这个唯一的实例
3.提供一个静态方法,获取这个实例对象
 */
public class Singleton05 {
    private static Singleton05 instance;
    private Singleton05(){
    }
    public static Singleton05 getInstance(){
        synchronized (Singleton05.class){//当前类为锁对象
            if(instance==null){

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                instance=new Singleton05();
            }
        }

        return instance;
    }
}

测试类:TestSingleton022


import java.util.concurrent.*;

public class TestSingleton022 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //单线情况下
//        Singleton04 singleton01=Singleton04.getInstance();
//        Singleton04 singleton02=Singleton04.getInstance();
//        System.out.println(singleton01==singleton02);//true
        //线程安全问题
        Callable<Singleton05> c = new Callable<Singleton05>(){
            @Override
            public Singleton05 call() throws Exception {
                return Singleton05.getInstance();
            }
        };

        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Singleton05> f1 = es.submit(c);
        Future<Singleton05> f2 = es.submit(c);

        Singleton05 s1 = f1.get();
        Singleton05 s2 = f2.get();

        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s2==s1);

        es.shutdown();

    }
}
静态内部类形式(适用于多线程)

既能保持延迟加载,还能保证线程安全(看注释)

/*
在内部类被加载和初始化时,才创建INSTANCE实例对象
静态内部类不会随着外部类的加载和初始化而初始化,它是要单独去加载和初始化
因为是在内部类加载和初始化时,创建的,因此是线程安全的
 */
public class Singleton07 {

    public Singleton07() {
    }

    private static class Inner{
        private static final Singleton07 INSTANCE=new Singleton07();
    }//内部类
    public static Singleton07 getInstance2(){
        return Inner.INSTANCE;
    }
}

目录
相关文章
|
1天前
|
设计模式 安全 测试技术
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
【C/C++ 设计模式 单例】单例模式的选择策略:何时使用,何时避免
67 0
|
1天前
|
设计模式 缓存 安全
单例设计模式的优缺点
单例设计模式的优缺点
34 0
|
1天前
|
设计模式 XML 存储
关于 ABAP 单例设计模式的一个冷门知识点
关于 ABAP 单例设计模式的一个冷门知识点
23 0
|
1天前
|
设计模式 安全 Java
【设计模式】2、设计模式分类和单例设计模式
【设计模式】2、设计模式分类和单例设计模式
28 0
|
1天前
|
设计模式 Java
26、Java 简单实现单例设计模式(饿汉式和懒汉式)
26、Java 简单实现单例设计模式(饿汉式和懒汉式)
30 2
|
1天前
|
设计模式 消息中间件 安全
多线程编程设计模式(单例,阻塞队列,定时器,线程池)(二)
多线程编程设计模式(单例,阻塞队列,定时器,线程池)(二)
35 1
|
1天前
|
设计模式 安全 Java
最简单的设计模式是单例?
单例模式可以说是Java中最简单的设计模式,但同时也是技术面试中频率极高的面试题。因为它不仅涉及到设计模式,还包括了关于线程安全、内存模型、类加载等机制。所以说它是最简单的吗?
58 3
最简单的设计模式是单例?
|
1天前
|
设计模式 安全 Java
在Java中即指单例设计模式
在Java中即指单例设计模式
22 0
|
6月前
|
设计模式 算法 Java
Java面试题 - 设计模式
Java面试题 - 设计模式
59 0
|
6月前
|
设计模式 存储
static应用之 单例设计模式(饿汉单例&懒汉单例)
本章我们来学习单例模式中的饿汉单例和懒汉单例,那么什么是单例模式呢?应用该模式的这个类永远只有一个实列,即一个类只能创建一个对象例如电脑上的任务管理器对象只需要一个就能解决问题,可以节省内存空间先定义一个类,把构造器私有如下图,先来看一下没有把构造器私有化的SingleInstance类,此时Test类中可以随意创建多个SingleInstance的实例化。 在SingleInstance类中用private修饰无参构造器,此时左边new方法报错了。我们在右边创建一个静态变量来存储对象,变量名为instan
27 0

热门文章

最新文章