java与设计模式-单例模式

本文介绍了Java中的单例模式,通过代码展示了如何利用私有构造函数和静态方法实现单例,以及在多线程环境下可能出现的问题。为解决并发问题,引入了双重校验锁(DCL)来确保线程安全并保持单例特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

     大家都知道,常用的设计模式有23种,这23中设计模式又分为三类,分别为创建型、结构型和行为型三类。其中创建型模式共5种,包括工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式;结构型模式共7种,包括适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式;行为性模式共11中,包括策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    今天给大家介绍一下创建型模式中的单例模式,单例模式顾名思义,一个类只能有一个对象实例,如何保证某个类只有一个实例呢?我们知道,java中,对象的创建是通过new关键字+构造函数完成的,构造函数是和类名相同的函数,一般用public 进行修饰,表示包的外部可见,如果我们把它改成private,那么外部也就不可见了,自然也就不能用new 关键字创建该类的实例对象了,这样就限制了外部创建该类的对象的权限,那么问题来了,如何创建类的实例呢?上面说了,既然不能在外部使用new 关键字了,类的内部还可以使用吗?答案是肯定的,请看下面的代码:

package com.lt.design.pattern;
import java.util.concurrent.CountDownLatch;
/**
 * Created by david on 2021/1/30 10:52
 * 单例模式-懒汉形式
 */
public class Singleton1 {
   /**
    * 单例模式的构造方法声明为私有的,外部不能使用new进行构造
    */
   private Singleton1() {
   }

   private static Singleton1 instance = null;
   /**
    * 多线程并发时,获取到的实例可能不相等
    *
    * @return
    */
   public static Singleton1 getInstance() {
      if (instance == null) {
         instance = new Singleton1();
      }
      return instance;
   }

   static Singleton1 instance3 = null;
   static Singleton1 instance4 = null;
   public static void main(String[] args) {

      final CountDownLatch countDownLatch = new CountDownLatch(2);
      Singleton1 instance1 = Singleton1.getInstance();
      Singleton1 instance2 = Singleton1.getInstance();
      System.out.println("instance1 == instance2:" + (instance1 == instance2));
      try {
         //多线程模式下获取单例
         new Thread(new Runnable() {
            public void run() {
               System.out.println("thread1 run:" + System.currentTimeMillis());
               instance3 = Singleton1.getInstance();
               countDownLatch.countDown();
            }
         }).start();
         new Thread(new Runnable() {
            public void run() {
               System.out.println("thread2 run:" + System.currentTimeMillis());
               instance4 = Singleton1.getInstance();
               countDownLatch.countDown();
            }
         }).start();
         //等待上面两个线程都执行完毕
         countDownLatch.await();
         System.out.println("instance3 == instance4:" + (instance4 == instance4));
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
}

 

上面的代码中,通过将构造函数设为private,另外提供了一个getInstance()方法来获取该类的实例对象,以实现单例,然而上面的代码中,getInstance()方法存在并发问题,多线程同时调用时可能会产生多个实例,违背了单例设计的原则,为了解决这个问题,我们将代码进行改进,如下:

 

package com.lt.design.pattern;

import java.util.concurrent.CountDownLatch;

/**
 * Created by david on 2021/1/30 10:52
 * 单例模式
 */
public class Singleton2 {
  /**
   * 单例模式的构造方法声明为私有的,外部不能使用new进行构造
   */
  private Singleton2() {
  }
  //原子操作,不会引起线程上下文的切换和调度
  private static volatile Singleton2 instance = null;

  /**
   * 双重校验锁
   *
   * @return
   */
  public static Singleton2 getInstance() {
    if (instance == null) {
      synchronized (Singleton2.class){
        if(instance == null){
          instance = new Singleton2();
        }
      }
    }
    return instance;
  }

  static Singleton2 instance3 = null;
  static Singleton2 instance4 = null;

  public static void main(String[] args) {

    final CountDownLatch countDownLatch = new CountDownLatch(2);
    Singleton2 instance1 = Singleton2.getInstance();
    Singleton2 instance2 = Singleton2.getInstance();

    System.out.println("instance1 == instance2:" + (instance1 == instance2));

    try {
      //多线程模式下获取单例
      new Thread(new Runnable() {
        public void run() {
          System.out.println("thread1 run:" + System.currentTimeMillis());
          instance3 = Singleton2.getInstance();
          countDownLatch.countDown();
        }
      }).start();

      new Thread(new Runnable() {
        public void run() {
          System.out.println("thread2 run:" + System.currentTimeMillis());
          instance4 = Singleton2.getInstance();
          countDownLatch.countDown();
        }
      }).start();

      //等待上面两个线程都执行完毕
      countDownLatch.await();
      System.out.println("instance3 == instance4:" + (instance4 == instance4));

    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

 

上面的代码中,getInstance()方法加入了双重校验锁机制,即保证了线程安全,又实现了每次返回的都为同一个对象。好了,关于单例模式,本文就介绍到了这里,当然,单例模式的设计还有其他几种方式,如内部类和枚举方式,有兴趣的同学可以进一步查阅资料进行学习。本文只代表作者自身观点,如有疑问,欢迎到评论区留言,共同交流共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值