栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java 创建单例模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 创建单例模式

首先创建单例模式要符合三个条件:1、一个类只能创建一个对象;2、该类自己创建这个对象;3、提供全局对外生成对象的方法。

1、饿汉模式        

public final class Singleton {
    private static Singleton instance=new Singleton();//该类自己创建实例
    private Singleton(){}//构造函数私有
    public static Singleton getInstance(){//通过公共方法对外提供服务
        return instance;
    }
}

分析:饿汉模式可以保证多线程下的生成对象是唯一的,原因是在类加载阶段执行的时候就就已经为该对象创建好内存空间,如果对象属性比较少,并且使用的地方较多,可以牺牲一些内存空间来使用饿汉模式来生成单例类对象。如果类属性比较多,那么创建饿汉模式单例对象就比较浪费内存空间了,推荐使用懒汉模式来创建单例类。

2、懒汉模式

      懒汉模式中的懒即懒加载,即对象使用时加载,避免在类加载的过程中占用内存空间来加载类对象,节省内存空间。

public final class Singleton {
    private static Singleton instance= null;
    private Singleton(){}//构造函数
    public static Singleton getInstance(){//通过该方法对外提供生成单例对象
        if(null == instance){//当instance为null时,则实例化对象
            instance = new Singleton();
        }
        return instance;//否则返回已经存在的对象
    }
}

        上述实现方式在单线程下是不会有问题的,但是在多线程情况下是线程不安全的。分析原因如下:假设线上A通过 if 判断 instance == null 的时候,线程B也执行到了该处代码,那么线程A和线程B就会生成两个对象,破坏了单例的定义。

        基于此需要通过synchronized来加上同步锁,来避免多线程访问的时候创建多个对象,代码示例如下:

public final class Singleton {
    private static Singleton instance= null;
    private Singleton(){}//构造函数
    public static Synchronized Singleton getInstance(){//通过该方法对外提供生成单例对象
        if(null == instance){//当instance为null时,则实例化对象
            instance = new Singleton();
        }
        return instance;//否则返回已经存在的对象
    }
}

加上同步锁以后,势必会降低产生对象的效率,另外每次请求创建对象的时候,只有在第一获取的时候需要创建对象,其他请求基本都不是null,并且产生多个对象是因为 if 判断处导致的,基于以上两点考虑如下实现方式:

public final class Singleton {
    private static Singleton instance= null;
    private Singleton(){}//构造函数
    public static Synchronized Singleton getInstance(){//通过该方法对外提供生成单例对象
        if(null == instance){//当instance为null时,则实例化对象
            Synchronized(Singleton.class){
                instance = new Singleton();
            }
        }
        return instance;//否则返回已经存在的对象
    }
}

但是以上实现还是有问题,依然会创建多个实例。这是由于当多个线程进入到 if 判断条件里,虽然有了synchronized同步锁,但是进入到判断条件里面的线程依然会依次获取到锁创建对象,然后再释放同步锁。所以还需在同步锁里面加一个判断条件即Double-check,代码示例如下:

public final class Singleton {
    private static Singleton instance= null;
    private Singleton(){}//构造函数
    public static Synchronized Singleton getInstance(){//通过该方法对外提供生成单例对象
        if(null == instance){//当instance为null时,则实例化对象
            Synchronized(Singleton.class){
                if(null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;//否则返回已经存在的对象
    }
}

        上述代码当出现指令重排序的时候,依然会存在问题,问题主要出现在 instance = new Singleton(); 的时候,正常的过程是 分配内存空间、属性初始化、对象执行内存空间。JVM为了提高并发效率可能破坏上述过程,导致多线程情况下获取的对象可能还没有完成初始化,进而导致属性不可用。这时候的synchronized同步锁可以保证有序性,可见性,但是不能保证有序性。

        volatile可以弥补synchronized的空缺, volatile变量的操作指令都不会被重排序。于是有了如下代  码示例:

public final class Singleton {
    private volatile static Singleton instance= null;
    private Singleton(){}//构造函数
    public static Synchronized Singleton getInstance(){//通过该方法对外提供生成单例对象
        if(null == instance){//当instance为null时,则实例化对象
            Synchronized(Singleton.class){
                if(null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;//否则返回已经存在的对象
    }
}

        上述实现单例的方式比较复杂容易出错。日常不推荐使用,推荐使用内部类的方式或者枚举的方式实现

        内部类的方式可以避免多线程下重复创建对象的发生,并且在第一次调用生成对象方法的时候创建对象,代码示例如下:

public final class Singleton {
  private Singleton() { 
  }
  // 内部类实现
  public static class InnerSingleton {
    private static Singleton instance=new Singleton();
  }
  public static Singleton getInstance() {
    return InnerSingleton.instance;
  }
}

另外枚举天生是单例的,常用枚举属于饿汉式,下面提供枚举方式实现懒汉式示例代码:

public class Sinleton {
    private static Sinleton instance = null;
    // 私有构造函数
    private Sinleton() {
    }
    public static Sinleton getInstance() {
        return SinletonEnum.SINLETON.getInstance();
    }
    private enum SinletonEnum {

        SINLETON;

        private Sinleton singleton;

        // JVM保证这个方法只调用一次
        SinletonEnum() {
            singleton = new Sinleton();
        }

        public Sinleton getInstance() {
            return singleton;
        }
    }
}

               

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/583983.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号