【问题标题】:safe publication, argument passing安全发布,参数传递
【发布时间】:2016-11-10 12:40:47
【问题描述】:

我有两个线程,每个线程都有自己的计数器:线程 A 有 counterA,线程 B 有 counterB。每个线程都必须使用两个计数器:线程 A 必须使用 counterA 和 counterB,线程 B 也必须同时使用两者。 我正在使用 AtomicInteger 并在两个线程之间共享计数器,我将它们作为参数传递给线程,每个线程将两个计数器存储在私有字段中。

// ...
AtomicInteger counterA = new AtomicInteger(0);
AtomicInteger counterB = new AtomicInteger(0);

Thread tA = new Thread(new RunnableA(counterA, counterB)); 
Thread tB = new Thread(new RunnableB(counterA, counterB)); 

// ... in the constructor of RunnableA ...
RunnableA(AtomicInteger counterA, AtomicInteger counterB) {
    this.counterA = counterA;
    this.counterB = counterB;
} 

//...
// The same for RunnableB

这是两个计数器中的safe publishing 吗? 安全发布是必要的,因为对对象的引用不够安全,无法在线程之间共享对象。 在这种情况下如何实现安全发布?

提前致谢。

【问题讨论】:

    标签: java thread-safety safe-publication


    【解决方案1】:

    “安全发布”一词不适用于此。安全发布是关于在构造函数中创建的状态的发布。在您的示例中,AtomicInteger 对象是在调用构造函数之前创建的。

    如果使用了其他类型,这可能安全也可能不安全,具体取决于是否以及如何发布 counterAcounterB 变量。但是,通过将计数器实现为AtomicInteger 对象,您无需担心发布问题。它们是原子/线程安全的,无论它们是如何发布的。唯一的可能关注可能是实例变量状态的发布。如果不查看其他班级,我们无法判断是否会发生这种情况。例如:

    • 变量是final 还是volatile
    • 如果它们是非易失性和非最终的,对它们的访问是否正确同步?
    • 在调用run() 之后它们是线程受限的吗?

    请注意,runnables 将在当前线程中实例化,而不是在调用tA.start()tB.start() 时(如果)创建的线程中。当start() 被调用时,当前线程的start() 调用和新线程的run() 调用之间有一个happens-before。这意味着可以保证将变量安全地发布到子线程本身。只有将变量发布到其他线程才值得关注。

    【讨论】:

    • “我无法立即看到它是怎么回事”——如果计数器在新创建的线程之外作为 Runnables 的实例变量被访问。不保证在构造函数中初始化的值在被访问之前是可见的,除非例如声明至少一个实例变量为 final,请参阅shipilev.net/blog/2014/safe-public-construction
    • 嗯是的...但是在构造函数返回之前,anything 怎么能访问这些变量呢?查看构造函数的代码。 (我确实知道安全发布意味着什么,final 只是实现它的一种方式。)
    • "当 start() 被调用时,在当前线程的 start() 调用和新线程的 run() 调用之间有一个happens-before。这意味着将变量安全发布给子进程线程本身是有保证的。”这是我觉得有用的一点。例如,不需要在 Runnable 中将“参数变量”(this.counterA、this.counterB)声明为 final,即使 Runnable 是在主线程中构造的,然后这些参数变量在创建的线程。
    • RunnableA 的变量 this.counterA、this.counterB 只被线程 A 使用,但对象 counterA 和 counterB 被两个线程使用(使用 .get()、.incrementAndGet() 方法) .
    • @Gibezynu Nu:如果仅从 Runnables 中访问变量,则代码将没问题,内存屏障和发生之前就足够了。我发表评论只是为了回答 Stephen C 的疑问,即不安全的初始化(不是发布!)实际上可能会发生。他似乎已经部分考虑到了这一点,因为现在答案已经改变并且怀疑已经消除。尽管如此,根据您的评论,我相信您的代码还可以,实际上您不需要实例变量 final (即使我没有看到任何不这样做的理由)。
    猜你喜欢
    • 2021-12-15
    • 2020-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多