美文网首页
Java多线程(6)

Java多线程(6)

作者: Cool_Pomelo | 来源:发表于2020-01-19 17:39 被阅读0次

Java多线程(6)

七种单例设计模式的设计

饿汉式

public class Singleton {

//    实例变量
    private byte[] data = new byte[1024];

//    定义实例对象的时候直接初始化
    private static Singleton instance = new Singleton();

//    私有构造函数
    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}


饿汉式的关键在于instance作为类变量并且直接得到了初始化,如果主动使用Singleton类,那么instance实例将会直接完成创建

insta作为类变量在类初始化的过程中会被收集劲< clinit >()方法中,该方法能够百分之百保证同步,也就是instance在多线程的情况下不可能被实例化两次

懒汉式

public final class Singleton {
    
    private byte[] data = new byte[1024];
    
//    定义实例,但是不直接初始化
    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if(null == instance){
            instance = new Singleton();
        }
        return instance;
    }
}


懒汉式就是使用类实例的时候再去创建

Singleton.class被初始化的时候instance并不会被实例化,但是getInstance方法放在多线程环境下分析,则会导致instance被实例化不止一次,并不能保证单例的唯一性

懒汉式 + 同步方法

public class Singleton {
    
    private byte[] data = new byte[1024];
    
    private static Singleton instance = null;

    private Singleton() {
    }

//    加入同步控制,每次只有一个线程能够进入
    public synchronized static Singleton getInstance() {
        if (null==instance){
            instance = new Singleton();
        }
        return instance;
    }
}


考虑到懒汉式无法保证实例的唯一性,此处使用synchronize关键字来修饰getInstance方法,来保证数据的同步性,但是这种方法性能低下。

Double - Check

public class Singleton {

    private byte[] data = new byte[1024];

    private static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {

//        当instance为null,进入同步代码块,避免了每次都要进入
        if (null == instance){


//            只有一个线程能够获得Singleton.class关联的锁
            synchronized (Singleton.class){
                if (null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


双重检查机制首次初始化的时候加锁,之后则允许多个线程同时进行getInstance调用获取类的实例

当两个线程发现null == instance时候,只有一个线程有资格进入同步代码块,完成instance的实例化,随后线程发现null == instance不成立时无需进行任何动作

Volatile + Double - Check

上述Double - Check写法看似解决了问题,但是有个很大的隐患。实例化对象的那行代码(instance = new Singleton();),实际上可以分解成以下三个步骤:

  • 分配内存空间
  • 初始化对象
  • 将对象指向刚分配的内存空间

但是有些编译器为了性能的原因,可能会将第二步和第三步进行重排序,顺序就成了:

  • 分配内存空间

  • 将对象指向刚分配的内存空间

  • 初始化对象

这种情况下对应到就是singleton已经不是null,而是指向了堆上的一个对象,但是该对象却还没有完成初始化动作。当后续的线程发现singleton不是null而直接使用的时候,就会出现意料之外的问题

可以使用volatile关键字修饰变量来解决无序写入产生的问题,因为volatile关键字的一个重要作用是禁止指令重排序,即保证不会出现内存分配、返回对象引用、初始化这样的顺序,从而使得双重检测真正发挥作用


public class Singleton {

    private byte[] data = new byte[1024];

    private volatile static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {

//        当instance为null,进入同步代码块,避免了每次都要进入
        if (null == instance){


//            只有一个线程能够获得Singleton.class关联的锁
            synchronized (Singleton.class){
                if (null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Holder方式

public class Singleton {
    
    private byte[] data = new byte[1024];

    private Singleton() {
    }
    
//    在静态内部类中持有Singleton实例,并且直接进行初始化
    private static class Holder{
        private static Singleton instance = new Singleton();
    }
    
    
    public static Singleton getInstance(){
        return Holder.instance;
    }
}


Holder方式完全借助了类加载的特点

此方式又可以保证数据的同步性,也能保证内存的可见性,指令顺序性,原子性

枚举方式

public enum Singleton {
    
    INSTANCE;
    
    private byte[] data = new byte[1024];
    
    Singleton()
    {
        System.out.println("INSTANCE will be 初始化");
    }
    
    public static void method(){
//        调用该方法会主动使用Singleton,INSTANCE将被实例化
    }
    
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

枚举类型不允许继承,同样是线程安全的且只能被实例化一次

相关文章

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • 带你搞懂Java多线程(六)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四)带...

  • Java多线程目录

    Java多线程目录 Java多线程1 线程基础Java多线程2 多个线程之间共享数据Java多线程3 原子性操作类...

  • Java多线程(6)

    Java多线程(6) 七种单例设计模式的设计 饿汉式 饿汉式的关键在于instance作为类变量并且直接得到了初始...

  • java多线程--Callable

    **移步[java多线程系列文章]Java多线程(二十二)---LockSupport工具Java 停止线程 一、...

  • android 多线程 — 线程的面试题和答案

    这里都是我从各个地方找来的资料,鸣谢: Java多线程干货系列—(一)Java多线程基础 JAVA多线程和并发基础...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

  • 带你搞懂Java多线程(四)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三) 什么是线程间的协作 线程之间...

  • Java基础(六)

    多线程 Java多线程并发 1.1 JAVA 并发知识库 1.2 JAVA 线程实现/创建方式 1.2.1 继承 ...

  • (五) volatile关键字

    Java多线程目录 1 背景 理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为...

网友评论

      本文标题:Java多线程(6)

      本文链接:https://www.haomeiwen.com/subject/asarzctx.html