正确使用volatile变量和synchronized块 [英] Proper use of volatile variables and synchronized blocks

查看:157
本文介绍了正确使用volatile变量和synchronized块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在java(或一般)中绕过线程安全。我有这个类(我希望符合POJO的定义),它也需要与JPA提供者兼容:

I am trying to wrap my head around thread safety in java (or in general). I have this class (which I hope complies with the definition of a POJO) which also needs to be compatible with JPA providers:

    public class SomeClass {

        private Object timestampLock = new Object();
        // are "volatile"s necessary?
        private volatile java.sql.Timestamp timestamp;
        private volatile String timestampTimeZoneName;

        private volatile BigDecimal someValue;

        public ZonedDateTime getTimestamp() {
            // is synchronisation necessary here? is this the correct usage?
            synchronized (timestampLock) {
                return ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.of(timestampTimeZoneName));
            }
        }

        public void setTimestamp(ZonedDateTime dateTime) {
            // is this the correct usage?
            synchronized (timestampLock) {
                this.timestamp = java.sql.Timestamp.from(dateTime.toInstant());
                this.timestampTimeZoneName = dateTime.getZone().getId();
            }
        }

        // is synchronisation required?
        public BigDecimal getSomeValue() {
            return someValue;
        }

        // is synchronisation required?
        public void setSomeValue(BigDecimal val) {
            someValue = val;
        }
    }

如代码中注释的行所述,必须将 timestamp timestampTimeZoneName 定义为 volatile 并且使用的 synchronized 块应该是什么?或者我应该只使用 synchronized 块而不是定义时间戳 timestampTimeZoneName as volatile 时间戳 timestampTimeZoneName 不应与另一个时间戳的。

As stated in the commented rows in the code, is it necessary to define timestamp and timestampTimeZoneName as volatile and are the synchronized blocks used as they should be? Or should I use only the synchronized blocks and not define timestamp and timestampTimeZoneName as volatile? A timestampTimeZoneName of a timestamp should not be erroneously matched with another timestamp's.

这个链接说


读取和写入对于声明为volatile
的所有变量都是原子的(包括long和b双变量)

Reads and writes are atomic for all variables declared volatile (including long and double variables)

我是否应该理解通过此代码访问 someValue 由于 volatile 定义,setter / getter是线程安全的吗?如果是这样,有没有更好的(我不知道这里有什么更好的意思)实现这个目的?

Should I understand that accesses to someValue in this code through the setter/getter are thread safe thanks to volatile definitions? If so, is there a better (I do not know what "better" might mean here) way to accomplish this?

推荐答案

要确定您是否需要同步,请尝试想象一个可以使用上下文切换来破坏代码的地方。

To determine if you need synchronized, try to imagine a place where you can have a context switch that would break your code.

在这种情况下,如果上下文切换发生在我放置注释的地方,那么在 getTimestamp()你'我将从每个时间戳类型中读取不同的值。

In this case, if the context switch happens where I put the comment, then in getTimestamp() you're going to be reading different values from each timestamp type.

此外,虽然赋值是原子的,但此表达式 java.sql.Timestamp.from( dateTime.toInstant()); 当然不是,所以你可以在 dateTime.toInstant()之间进行上下文切换,并调用。简而言之,你肯定需要同步块。

Also, although assignments are atomic, this expression java.sql.Timestamp.from(dateTime.toInstant()); certainly isn't, so you can get a context switch inbetween dateTime.toInstant() and the call to from. In short you definitely need the synchronized blocks.

synchronized (timestampLock) {
    this.timestamp = java.sql.Timestamp.from(dateTime.toInstant());
    //CONTEXT SWITCH HERE
    this.timestampTimeZoneName = dateTime.getZone().getId();
}

synchronized (timestampLock) {
    return ZonedDateTime.ofInstant(timestamp.toInstant(), ZoneId.of(timestampTimeZoneName));
}

就易变性而言,我很确定它们是必需的。您必须保证每个线程肯定会获得变量的最新版本。

In terms of volatile, I'm pretty sure they're required. You have to guarantee that each thread definitely is getting the most updated version of a variable.

这是volatile的合约。虽然它可能被同步块覆盖,并且在这里实际上并不需要,但无论如何都要写。如果synchronized块已经执行了volatile的工作,则VM将不会执行两次保证。这意味着挥发性不会再花费你了,而且它是一个非常好的闪光灯,对程序员说:我用在多个线程中。

This is the contract of volatile. And although it may be covered by the synchronized block, and volatile not actually necessary here, it's good to write anyway. If the synchronized block does the job of volatile already, the VM won't do the guarantee twice. This means volatile won't cost you any more, and it's a very good flashing light that says to the programmer: "I'M USED IN MULTIPLE THREADS".

对于 someValue :如果这里没有同步块,那么挥发绝对是必要的。如果在一个线程中调用一个集合,则另一个线程没有队列告诉它可能已在此线程之外更新。因此它可能使用旧的缓存值。如果它假设单线程,JIT可以做很多有趣的优化。可以简单地破坏你的程序。

For someValue: If there's no synchronized block here, then volatile is definitely necessary. If you call a set in one thread, the other thread has no queue that tells it that may have been updated outside of this thread. So it may use an old and cached value. The JIT can do a lot of funny optimizations if it assumes single thread. Ones that can simply break your program.

现在我不完全确定此处是否同步 。我的猜测是否定的。无论如何我会添加它以保证安全。或者你可以让java担心同步并使用 http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html

Now I'm not entirely certain if synchronized is required here. My guess is no. I would add it anyway to be safe though. Or you can let java worry about the synchronization and use http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html

这篇关于正确使用volatile变量和synchronized块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆