专栏首页JVMGCJava安全的发布对象
原创

Java安全的发布对象

安全发布对象

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型域或者AtomicReference对象中
  • 将对象的引用保存到某个正确构造对象的final类型域中
  • 将对象的引用保存到一个由锁保护的域中

Spring 框架中,Spring管理的类都是单例模式。如何保证一个实例只被初始化一次,且线程安全?通过不同单例的写法,具体描述安全发布对象的四种方法:

在静态初始化函数中初始化一个对象的引用(不推荐)

package com.rumenz.task.single;


//线程安全
//饿汉模式
//静态代码块初始化
public class SingletonExample {
    private SingletonExample(){
        //初始化操作
    }
    private static SingletonExample singletonExample=null;

    static {
        singletonExample=new SingletonExample();
    }

    public static SingletonExample getInstance(){
        return singletonExample;
    }
}

//或者
package com.rumenz.task.single;
//线程安全
//饿汉模式
//静态代码块初始化
public class SingletonExample {
    private SingletonExample(){

        //初始化操作

    }
    private static SingletonExample singletonExample=new SingletonExample();



    public static SingletonExample getInstance(){
        return singletonExample;
    }
}

缺点:用不用都会初始化对象,如果初始化工作较多,加载速度会变慢,影响系统性能。

将对象的引用保存到volatile类型或AtomicReference对象中(推荐)

package com.rumenz.task.single;

//线程安全
//懒汉模式
public class SingletonExample1 {

    private SingletonExample1() {
        //初始化操作
    }
    // 1、memory = allocate() 分配对象的内存空间
    // 2、ctorInstance() 初始化对象
    // 3、instance = memory 设置instance指向刚分配的内存
    // 单例对象 volatile + 双重检测机制 -> 禁止指令重排
    private volatile static SingletonExample1 singletonExample1=null;
    //静态工厂方法
    public static SingletonExample1 getInstance(){
        if(singletonExample1==null){ //双重检测
            synchronized(SingletonExample1.class){ //同步锁
                if(singletonExample1==null){
                    singletonExample1=new SingletonExample1();
                }
            }
        }
        return singletonExample1;
    }
}


//或者

package com.rumenz.task.single;

import java.util.concurrent.atomic.AtomicReference;

class SingletonAtomicReference<T> {
    /**
     * Implement the singleton using an AtomicReference.
     */
    public static AtomicReference<SingletonAtomicReference> sSingletonAR =
            new AtomicReference<>(null);

    /**
     * Define a non-static field.
     */
    private T mField;

    /**
     * * @return The value of the field.
     */
    public T getField() {
        return mField;
    }

    /**
     * Set and return the value of the field.
     */
    public T setField (T f) { return mField = f; }

    /**
     * The static instance() method from the Singleton pattern.
     */
    public static <T> SingletonAtomicReference instance() {
        // Get the current value of the singleton.
        SingletonAtomicReference<T> singleton = sSingletonAR.get();

        // Run this code if the singleton is not yet initialized.
        if (singleton == null) {

            singleton = new SingletonAtomicReference<>();
            //CAS
            if (!sSingletonAR.compareAndSet(null, singleton))
                singleton = sSingletonAR.get();
        }
        // Return the singleton's current value.
        return singleton;
    }
}

class R{
    private Integer age;

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public R(Integer age) {
        this.age = age;
    }

    public static void main(String[] args) {
        R o = (R)SingletonAtomicReference.instance().setField(new R(300));

        System.out.println(o.getAge());


    }
}

优点:按需加载 缺点:第一次初始化的时候可能会比较慢

AtomicReference使用场景

private static AtomicReference<DBConnector> instance = new AtomicReference<>();

public static DBConnector getDBConnector(DBConfig dBConfig) {
    // First try
    DBConnector con = instance.get();
    if (con == null) {
        con = // ...

        if (instance.compareAndSet(null, con)) {
            // Successful swap, return our copy
            return con;
        } else {
            // Lost the race, poll again
            return instance.get():
        }
    }

    // Already present value
    return con;
}

通过synchronized(不推荐)

package com.rumenz.task.single;

public class SingletonExample3 {
    //私有构造函数
    private SingletonExample3(){
        //初始化操作
    }

    private static SingletonExample3 singletonExample3=null;
    //静态的工厂方法
    public static synchronized SingletonExample3 getSingletonExample3(){
         if(singletonExample3==null){
             singletonExample3=new SingletonExample3();
         }
         return singletonExample3;
    }
}

缺点:每次进入getSingletonExample3都会加锁,耗费资源,故不推荐使用。

枚举(推荐)

package com.rumenz.task.single;

public class SingletonExample4 {

    //私有构造函数
    private SingletonExample4(){
       //初始化
    }
    public static SingletonExample4 getSingletonExample4(){
        return Singleton.INSTANCE.getSingleton();
    }
    private enum Singleton{
        INSTANCE;
        private SingletonExample4 singleton;
        Singleton(){
            singleton=new SingletonExample4();
        }
        public SingletonExample4 getSingleton(){
            return singleton;
        }

    }
}

优点:天然线程安全,可防止反射生成实例,推荐使用

将对象的引用保存到一个由锁保护的域中

public class SingletonExample {
    private static final Map<String, SingletonExample> MAP = new ConcurrentHashMap<>();
 
    private String fileLoc;
     
    private SingletonExample(String fileLoc) {
        this.fileLoc = fileLoc;
    }
 
    public static SingletonExample getSingletonInst(String index, String fileLocation) {
        return MAP.computeIfAbsent(index, k -> new SingletonExample(fileLocation));
    }
}
wx.jpg

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java安全发布对象总结-0

    在类的外部线程都能访问到这个state,这样发布对象是不安全,我们无法保证外部的线程不去修改state,从而造成state状态的错误。

    用户2032165
  • 安全发布对象-发布与逸出

    简单来说就是提供一个对象的引用给作用域之外的代码。比如return一个对象,或者作为参数传递到其他类的方法中。

    全菜工程师小辉
  • Java并发编程(3)- 如何安全发布对象

    在这个例子中,我们通过new对象得到了对象实例。获得这个对象后,我们可以调用getStates()方法得到私有属性的引用,这样就可以在其他任何线程中,修改该属性...

    端碗吹水
  • Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系。 什么是发布?简...

    用户1148394
  • Java并发编程与高并发之安全发布对象

      如果不正确的发布了可变对象,会造成两种错误,首先是发布线程以外的任何线程都可以看到被发布对象的过期的值。其次呢,线程看到的被发布对象的引用是最新的,然而呢,...

    别先生
  • Java-安全发布

     发布是一个动词,是去发布对象。而对象,通俗的理解是:JAVA里面通过 new 关键字 创建一个对象。

    Fisherman渔夫
  • 并发编程-08安全发布对象之发布与逸出

    发布对象: 使一个对象能够被当前范围之外的代码所使用,日常开发中比较常见的比如通过类的非私有方法返回对象的引用,或者通过公有的静态变量发布对象 等都属于发布对象

    小小工匠
  • 慕课网高并发实战(五)- 安全发布对象

    不正确的发布可变对象导致的两种错误: 1.发布线程意外的所有线程都可以看到被发布对象的过期的值 2.线程看到的被发布对象的引用是最新的,然而被发布对象的状态...

    Meet相识
  • JAVA对象布局

    注:启用+UseCompressedOops开启指针压缩,对象头长度为12BYTE,数组头长度为16BYTE。 另外以下指针压缩到4BYTE

    路过君
  • 并发编程-09安全发布对象+单例模式详解

    上篇文章并发编程-08安全发布对象之发布与逸出中简单的描述了下对象发布和逸出的概念,并通过demo演示了不安全发布对象对象逸出(this引用逸出)。 那该如何安...

    小小工匠
  • JAVA对象布局之对象头(Object Header)

    2.调用ClassLayout.parseInstance().toPrintable()

    Java宝典
  • JAVA对象布局之对象头(Object Header)

    2.调用ClassLayout.parseInstance().toPrintable()

    Java宝典
  • Java 并发编程(四):如何保证对象的线程安全性

    先让我吐一句肺腑之言吧,不说出来会憋出内伤的。《Java 并发编程实战》这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住。因为第四章“对象...

    沉默王二
  • Java对象内存布局

    对象实际数据包括了对象的所有成员变量,其大小由各个成员变量的大小决定,,比如:byte和boolean是1个字节,short和char是2个字节,int和flo...

    诺浅
  • 四、Java对象的内存布局

    上篇博客介绍的对象的创建过程,本文来介绍一下对象的组成结构。 在HotSpot虚拟机中,对象在内存中的布局划分为3个区域:对象头(Header),实例数据(...

    栋先生
  • java安全编码指南之:对象构建

    程序员肯定是不缺对象的,因为随时都可以构建一个,对象多了肯定会出现点安全问题,一起来看看在java的对象构建中怎么保证对象的安全性吧。

    程序那些事
  • 高并发之——如何安全的发布对象(含各种单例代码分析)

    发布对象:使一个对象能够被当前范围之外的代码所使用。 对象溢出:是一种错误的发布,当一个对象还没有构造完成时,就使它被其他线程所见。

    冰河
  • Java 虚拟机:Java对象的内存布局

    在 Java 程序中,我们拥有多种新建对象的方式。除了最为常见的 new 语句之外,我们还可以通过反射机制、Object.clone 方法、反序列化以及 Uns...

    码农架构
  • 线程安全集合类中的对象是安全的么?

    之前的文章Java并发BUG基础篇中提到过线程安全的集合类如CopyOnWriteArrayList、ConcurrentHashMap等的使用,以及线程安全类...

    FunTester

扫码关注云+社区

领取腾讯云代金券