并发编程-08安全发布对象之发布与逸出

简介: 并发编程-08安全发布对象之发布与逸出

2019080611330380.jpg



脑图

20190222165920930.png



概念


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

对象逸出: 首先需要明确的是对象逸出是一种错误的发布方式当一个对象还没有构造完成时,就使它被其他线程所见


示例

不安全的发布对象Demo

package com.artisan.example.publish;
import com.artisan.anno.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@NotThreadSafe
public class UnSafePublishObjectDemo {
  // 私有变量
  private String name = "artisan";
  // 通过public访问级别的方法getName发布了类的域,在类的外部,任何线程都可以访问这个域
  // 这样发布的对象是不安全的,因为我们无法得知其他线程是否会修改这个域导致该类里数据的错误
  public String  getName() {
    return name;
  }
  public static void main(String[] args) {
    // 通过new实例化UnSafePublishObjectDemo
    UnSafePublishObjectDemo unSafePublishObjectDemo = new UnSafePublishObjectDemo();
    // 调用getName()方法得到私有属性的引用
    String name = unSafePublishObjectDemo.getName();
    log.info("name:{}",name);
    // 假设有第二个线程去修改name属性的值
    String name2 = unSafePublishObjectDemo.getName();
    name2 = "小工匠";
    log.info("name:{}",name2);
  }
}


上面的代码里,通过new对象初始化了UnSafePublishObjectDemo对象。然后调用getName()方法获取到了私有属性的引用,这样就可以在其他任何线程中,修改该属性的值。这样将会导致我们在其他线程中,获取该属性的值时是不确定的,因为并不能得知该属性的值是否已被其他线程所修改过,所以这就是不安全的对象发布。


对象逸出Demo

package com.artisan.example.publish;
import com.artisan.anno.NotRecommand;
import com.artisan.anno.NotThreadSafe;
import lombok.extern.slf4j.Slf4j;
/**
 * 
 *  对象逸出示例,在对象构造完成之前,不可以将其发布
 * @author yangshangwei
 *
 */
@Slf4j
@NotThreadSafe
@NotRecommand
public class ObjectEscapeDemo {
  private int thisCanBeEscape = 0;
    public ObjectEscapeDemo() {
        new InnerClass();
    }
    private class InnerClass {
      // this引用的逸出
      // 内部类的构造器里包含了对封装实例的隐含引用,这样在对象没有被正确构造完成之前就会被发布,由此会导致不安全的因素在里面
        public InnerClass() {
            log.info("{}", ObjectEscapeDemo.this.thisCanBeEscape);
        }
    }
    public static void main(String[] args) {
        new ObjectEscapeDemo();
    }
}

上述代码中,内部类的构造器里包含了对封装实例的隐含引用,这样在对象没有被正确构造完成之前就会被发布,由此会导致不安全的因素在里面。


其中一个就是导致this引用在构造期间逸出的错误,它是在构造函数构造过程中启动了一个线程,无论是显式启动还是隐式启动,都会造成this引用的逸出。


新线程总会在所属对象构造完毕之前就已经看到它了,所以如果要在构造函数中创建线程,那么不要启动它,而是应该采用一个专有的start,或是其他初始化的方式统一启动线程。


这里其实我们可以使用工厂方法和私有构造函数来完成对象创建和监听器的注册等等来避免不正确的发布。


小结


不正确的发布可变对象导致的两种错误:

  • 发布线程以外的所有线程都可以看到被发布对象的过期的值
  • 线程看到的被发布对象的引用是最新的,然而被发布对象的状态却是过期的


代码


https://github.com/yangshangwei/ConcurrencyMaster

相关文章
|
机器学习/深度学习 算法 搜索推荐
【算法设计与分析】再探大O渐近表示法 | 增长顺序 | Big O | Big Omega | Big Order
【算法设计与分析】再探大O渐近表示法 | 增长顺序 | Big O | Big Omega | Big Order
361 0
|
前端开发 JavaScript API
2025年前端框架是该选vue还是react?有了大模型-例如通义灵码辅助编码,就不用纠结了!vue用的多选react,react用的多选vue
本文比较了Vue和React两大前端框架,从状态管理、数据流、依赖注入、组件管理等方面进行了详细对比。当前版本和下载量数据显示React更为流行,但Vue在国内用户量增长迅速。Vue 3通过组合式API提供了更灵活的状态管理和组件逻辑复用,适合中小型项目;React则更适合大型项目和复杂交互逻辑。文章还给出了选型建议,强调了多框架学习的重要性,认为技术问题已不再是选型的关键,熟悉各框架的最佳实践更为重要。
8747 1
|
Ubuntu 安全 网络协议
|
11天前
|
数据采集 人工智能 安全
|
7天前
|
机器学习/深度学习 人工智能 前端开发
构建AI智能体:七十、小树成林,聚沙成塔:随机森林与大模型的协同进化
随机森林是一种基于决策树的集成学习算法,通过构建多棵决策树并结合它们的预测结果来提高准确性和稳定性。其核心思想包括两个随机性:Bootstrap采样(每棵树使用不同的训练子集)和特征随机选择(每棵树分裂时只考虑部分特征)。这种方法能有效处理大规模高维数据,避免过拟合,并评估特征重要性。随机森林的超参数如树的数量、最大深度等可通过网格搜索优化。该算法兼具强大预测能力和工程化优势,是机器学习中的常用基础模型。
338 164