zoukankan      html  css  js  c++  java
  • 设计模式之单例模式

    一、单例模式(singleton)

    是Java设计模式中最常用的模式之一,是面试过程中经常考察的点,需要手写单例。单是唯一,例是实例,即某个类在系统中只能存在唯一的一个实例对象可供使用。

    二、要点

    1、 构造器私有化,由于类的实例唯一,所以外界不能通过new关键字创建实例对象

    2、 在类的内部创建实例,并定义一个静态成员变量保存类的实例

    3、 需要向外界提供获得实例的方式

    a) 直接暴露

    b) 通过get方法获取

    三、几种常见的单例模式,一般分为两类:饿汉式和懒汉式

    饿汉式

    随着类的加载不管需不需要都会直接创建实例对象,不存在线程安全问题。

    a. 直接实例化 (简单直观)
    /**
    * 单例模式之直接实例方式
    * @author lux81
    *
    */
    public class Singleton1 {
       //静态常量保存实例对象
    public static final Singleton1 INSTANCE = new Singleton1();
    //构造器私有化
    private Singleton1() {
    System.out.println("直接实例化");
    }
    }
    //调用测试
    public class TestSingleton1 {
    public static void main(String[] args) {
           //直接通过类名.属性 调用
    Singleton1 instance = Singleton1.INSTANCE;
    System.out.println(instance);
    }
    }
    b. 枚举式 (最简洁)
    /**
    * 单例模式之枚举方式
    * 枚举类型 表示该类型的对象是有限的几个
    * 我们可以限定为一个 就成了单例
    * @author lux81
    *
    */
    public enum Singleton2 {
    INSTANCE;
    //添加一个测试方法
    public void sayHello() {
    System.out.println("hello");
    }
    }

    //测试类
    public class TestSingleton2 {
    public static void main(String[] args) {
    Singleton2 instance2 = Singleton2.INSTANCE;
    //调用测试方法 输出 hello
    instance2.sayHello();
    //直接输出实例对象名
    System.out.println(instance2);
    }
    }

     

    c. 静态代码块 (适合复杂实例化)

    在实例化对象的时候可能会初始化一些数据

    /**
    * 单例模式之静态代码块方式
    * 加入在实例化对象时需要初始化一些配置信息可以使用该方式
    * @author lux81
    *
    */
    public class Singleton3 {
    public static final Singleton3 INSTANCE;
    //测试从配置文件中获取初始化数据
    private String info;

    static {
    //加载配置文件
    Properties prop = new Properties();
    try {
    prop.load(Singleton3.class.getClassLoader().getResourceAsStream("test.properties"));
    } catch (IOException e) {
    throw new RuntimeException(e);
    }

    //实例化对象 并获取初始化数据
    INSTANCE = new Singleton3(prop.getProperty("name"));
    }

    private Singleton3(String info) {
    this.info = info;
    }
    //setter和 getter方法略
    }

    //测试
    public class TestSingleton3 {
    public static void main(String[] args) {
    Singleton3 instance3 = Singleton3.INSTANCE;
           //调用方法获取配置文件 name属性值
    System.out.println(instance3.getInfo());
    }
    }

     

    懒汉式

    延迟创建或者是需要的时候才创建

    a. 线程不安全(适用于单线程)
    /**
    * 单例模式之懒汉式(线程不安全,适合单线程)
    * @author lux81
    *
    */
    public class Singleton4 {
    private static Singleton4 instance;

    private Singleton4() {
    }

    //提供给外面获取实例对象的get方法
    public static Singleton4 getInstance() {
    //如果对象不存在则创建,已经存在直接返回
    if (instance == null) {
               //此处如果在多线程的情况下,会出现创建多个不同对象的情况
               try {
    Thread.sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    instance = new Singleton4();
    }
    return instance;
    }
    }

    //测试
    public class TestSingleton4 {
    public static void main(String[] args) {
    Singleton4 s1 = Singleton4.getInstance();
    Singleton4 s2 = Singleton4.getInstance();
    System.out.println(s1 == s2); //返回结果 true
           
           //使用多线程方式测试
           Callable<Singleton4> c = new Callable<Singleton4>() {
    @Override
    public Singleton4 call() throws Exception {
    return Singleton4.getInstance();
    }
    };
           //定义一个线程池 数量设置为2
    ExecutorService es = Executors.newFixedThreadPool(2);
    Future<Singleton4> f3 = es.submit(c);
    Future<Singleton4> f4 = es.submit(c);

    Singleton4 s3 = f3.get();
    Singleton4 s4 = f4.get();

    System.out.println(s3 == s4); //结果为false

    es.shutdown();
    }
    }

     

    b. 线程安全(适用于多线程)

    在方式a基础上进行优化

    /**
    * 单例模式之懒汉式(线程安全)
    * @author lux81
    *
    */
    public class Singleton5 {
    private static Singleton5 instance;

    private Singleton5() {
    }

    //提供给外面获取实例对象的get方法
    public static Singleton5 getInstance() {
           //如果已经存在对象 就不需要再加同步锁
    if (instance == null) {
    //加上同步代码块 保证线程安全
    synchronized (Singleton5.class) {
    if (instance == null) {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    instance = new Singleton5();
    }
    }
    }
    return instance;
    }
    }
    //测试
    public class TestSingleton5 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
    Callable<Singleton4> c = new Callable<Singleton4>() {
    @Override
    public Singleton4 call() throws Exception {
    return Singleton4.getInstance();
    }
    };
    ExecutorService es = Executors.newFixedThreadPool(2);
    Future<Singleton4> f1 = es.submit(c);
    Future<Singleton4> f2 = es.submit(c);

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

    System.out.println(s1 == s2); //返回结果为 true

    es.shutdown();
    }
    }

     

    c. 静态内部类方式(适用于多线程)

    方式b看起来有点复杂,而静态内部类方式既能实现延迟加载,也能保证线程安全

    /**
    * 单例模式之懒汉式(静态内部类)
    * 只有在调用getInstance()方法时才会 加载和初始化内部类,进而才会创建实例对象
    * 静态内部类不会随着外部类的加载和初始化而初始化,是独立的,单独加载和初始化
    * @author lux81
    *
    */
    public class Singleton6 {
    private Singleton6() {
    }
    //定义静态内部类 创建实例化对象
    private static class Inner{
    private static final Singleton6 INSTANCE = new Singleton6();
    }
    //还是通过get方法获取
    public static Singleton6 getInstance() {
    return Inner.INSTANCE;
    }
    }

     

  • 相关阅读:
    Generate Parentheses
    Length of Last Word
    Maximum Subarray
    Count and Say
    二分搜索算法
    Search Insert Position
    Implement strStr()
    Remove Element
    Remove Duplicates from Sorted Array
    Remove Nth Node From End of List
  • 原文地址:https://www.cnblogs.com/coderLeo/p/13150054.html
Copyright © 2011-2022 走看看