专业编程基础技术教程

网站首页 > 基础教程 正文

本文将给大家介绍java中设计模式——单例模式

ccvgpt 2024-08-05 12:28:20 基础教程 13 ℃

单例模式:保证在?个JVM中,该对象只有?个实例存在;

适?场景:

本文将给大家介绍java中设计模式——单例模式

1、某些类创建?较频繁,对于?些?型的对象,这是?笔很?的系统开销。

2、省去了new操作符,降低了系统内存的使?频率,减轻GC压?。

3、有些类如交易所的核?交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(?如?个军队出现了多个司 令员同时指挥,肯定会乱成?团),所以只有使?单例模式,才能保证核?交易服务器独?控制整个流程。

代码:

 1 public class Singleton{
 2      //持有私有静态实例,防止被引用,赋值为null,目的就是实现延迟加载
 3     private static Singleton instance = null;
 4 
 5 
 6     //私有构造方法,防止被实例化。
 7   private Singleton(){
 8      }
 9    
10   //创建实例
11   public static Singleton getInstance()
12 {
13       if (instance=null){
14       instance=new Singleton();
15        
16 }
17 return  instance;
18 //如果该对象被?于序列化,可以保证对象在序列化前后保持?致
19    public Object readReaslove(){
20    return instance;
21 }
22 
23 }
24 }

分类:

1.饿汉式:类初始化时创建单例,线程安全,但占用内存空间大,适?于占内存?的场景,否则推荐使?懒汉式延迟加载;

   public class Singleton{

   private static Singleton instance = new Singleton();
//构造器私有, 外部就无法new这个对象,保证一个类中只有一个实例对象
private Singleton(){}

   public static Singleton newInstance(){
            return  instance;
}

}

2.懒汉式:需要单例实例的时候再创建,需要考虑线程安全(性能不太好)

懒汉式单例如何保证线程安全呢?

通过 synchronized 关键字加锁保证线程安全, synchronized 可以添加在?法上?,也可以添加在代码块上?,这?演示添加在?法上?,存在的问题是 每?次调? getInstance 获取实例时都需要加锁和释放锁,这样是?常影响性能的。

 1 public class Singleton{
 2   private static Singleton instance = null;//定义一个静态变量指向自己
 3   private Singleton(){}//私有化构造方法
 4   public static synchronized Singleton newInstance(){//加锁,
 5       if (instance == null){
 6          instance = new Singleton();
 7 }
 8 }
 9     return instance;
10 
11 }

3.双重检验锁:假如两个线程A、B,A执?了if (instance == null)语句,它会认为单例对象没有创建,此时线程切到B也执?了同样的语句,B也认为单例对象没有创建,然后两个线程依次执?同步代码块,并分别创建了?个单例对象。

这?的双重检查是指两次?空判断,所指的是 synchronized 加锁,为什么要进?双重判断,其实很简单,第?重判断,如果实例已经存在,那么就不再需要进?同步操作,?是直接返回这个实例,如果没有创建,才会进?同步块,同步块的?的与之前相同,?的是为了防?有多个线程同时调?时,导致?成多个实例,有了同步块,每次只能有?个线程调?访问同步块内容,当第?个抢到锁的调?获取了实例之后,这个实例就会被创建,之后的所有调?都不会进?同步块,直接在第?重判断就返回了单例。

关于内部的第?重空判断的作?,当多个线程?起到达锁位置时,进?锁竞争,其中?个线程获取锁,如果是第?次进?则为 null,会进?单例对象的创建,完成后释放锁,其他线程获取锁后就会被空判断拦截,直接返回已创建的单例对象。其中最关键的?个点就是 volatile 关键字的使?,关于 volatile 详细介绍可以直接搜索 volatile 关键字即可,有很多写的?常好的?章,这?不做详细介绍,简单说明?下,双重检查锁中使? volatile 的两个重要特性:可?性、禁?指令重排序

这?为什么要使? volatile ?

这是因为 new 关键字创建对象不是原?操作,创建?个对象会经历下?的步骤:

1. 在堆内存开辟内存空间

2. 调?构造?法,初始化对象

3. 引?变量指向堆内存空间

代码:

 1 public class Singleton{
 2    private  static  volatile Singleton instance = null;
 3    private Singleton(){}
 4   public static Singleton getInstance(){
 5    if (instance == null)
 6   {     synchronized (Singleton.class){
 7            if (instance == null){
 8     instance = new Singleton();
 9 
10 }
11    }
12 }
13 return instance;
14 }

4.静态内部类:可以同时保证延迟加载和线程安全。静态内部类单例是如何实现懒加载的呢?(懒加载 :使?的时候再创建对象)?先,我们先了解下类的加载时机。

虚拟机规范要求有且只有 5 种情况必须?即对类进?初始化(加载、验证、准备需要在此之前开始):

1. 遇到 new 、 getstatic 、 putstatic 、 invokestatic 这 4 条字节码指令时。?成这 4 条指令最常?的 Java 代码场景是:使? new 关键字实例化对象的时候、读取或设置?个类的静态字段(final 修饰除外,被final 修饰的静态字段是常量,已在编译期把结果放?常量池)的时候,以及调??个类的静态?法的时候。

2. 使? java.lang.reflect 包?法对类进?反射调?的时候。

3. 当初始化?个类的时候,如果发现其?类还没有进?过初始化,则需要先触发其?类的初始化。

4. 当虚拟机启动时,?户需要指定?个要执?的主类(包含 main()的那个类),虚拟机会先初始化这个主类。

5. 当使? JDK 1.7 的动态语??持时,如果?个java.lang.invoke.MethodHandle 实例最后的解析结果是REF_getStatic 、 REF_putStatic 、 REF_invokeStatic 的?法句柄,则需要先触发这个?法句柄所对应的类的初始化。

这 5 种情况被称为是类的主动引?,注意,这?《虚拟机规范》中使?的限定词是 "有且仅有",那么,除此之外的所有引?类都不会对类进?初始化,称为被动引?。静态内部类就属于被动引?的情况。当 getInstance()?法被调?时,InnerClass 才在 Singleton 的运?时常量池?,把符号引?替换为直接引?,这时静态对象 INSTANCE 也真正被创建,然后再被 getInstance()?法返回出去,这点同饿汉模式。

那么 INSTANCE 在创建过程中?是如何保证线程安全的呢?在《深?理解 JAVA 虚拟机》中,有这么?句话:虚拟机会保证?个类的 <clinit>() ?法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化?个类,那么只会有?个线程去执?这个类的 <clinit>() ?法,其他线程都需要阻塞等待,直到活动线程执?<clinit>() ?法完毕。如果在?个类的 <clinit>() ?法中有耗时很?的操

作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执? <clinit>() ?法后,其他线程唤醒之后不会再次进? <clinit>() ?法。同?个加载器下,?个类型只会初始化?次。),在实际应?中,这种阻塞往往是很隐蔽的。

从上?的分析可以看出 INSTANCE 在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯?性,同时也延迟了单例的实例化。

 1 public class Singleton{
 2     private Singleton(){}//私有化构造方法
 3     public  static Singleton getInstance(){//对外提供获取实例的公共方法
 4       return InnerClass.Instance;}
 5      private static class InnerClass{//定义静态内部类
 6         private final static Singleton Instance=new Singleton;
 7 
 8 }
 9 
10 }

5.枚举:

使?枚举除了线程安全和防?反射调?构造器之外,还提供了?动序列化机制,防?反序列化的时候创建新的对象。

1 public enum Singleton{
2  instance; 
3 public void whateverMethod(){} 
4 }

文章来自https://www.cnblogs.com/zfj0318/p/16328903.html

最近发表
标签列表