4

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;
        }
    }

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.

This link says

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

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?

Gray
  • 115,027
  • 24
  • 293
  • 354
mevtagezer
  • 77
  • 7
  • Are you going to use `SomeClass` as a JPA entity? – Yuri May 15 '14 at 12:52
  • yes, I will use it as a JPA entity – mevtagezer May 15 '14 at 12:53
  • `EntityManager` and `@Entity` classes should not be exposed to concurrent access from multiple threads. Check [Transactions and Concurrency chapter from Hibernate docs](http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/transactions.html). All in all I suggest applying synchronization to your business logic. – Yuri May 15 '14 at 13:14
  • I want to experiment on asynchronous updating of entities from the database. Like a worker thread periodically refreshing select entities. – mevtagezer May 15 '14 at 13:31

3 Answers3

1

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

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.

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.

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".


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.

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

Cruncher
  • 7,641
  • 1
  • 31
  • 65
  • what about "volatile"s? do I need them for timestamp and/or timestampTimeZoneName? and for getting and setting someValue? – mevtagezer May 15 '14 at 13:00
  • @mevtagezer see last paragraph – Cruncher May 15 '14 at 13:00
  • since getter gives out a new ZonedDateTime and setter replaces the private fields. it "feels" like the volatile definitions for timestamp and timestampTimeZoneName are not necessary. would it be if these fields were to be used another place in the class (other than getter/setter)? – mevtagezer May 15 '14 at 13:15
  • 1
    @mevtagezer It's certainly possible that it may not matter in this case. I'm not 100% on the details. However, if it doesn't matter, then that means the synchronized block does the job of the volatile variable, and thus the volatile variables incurs no more overhead. Meaning you get it for free. However having volatile in the definition is helpful to the programmer. It tells them: "Hey, this may be accessed across multiple threads!". Which is a nice thing to know. – Cruncher May 15 '14 at 13:25
  • any idea on volatility and sychronisation requirement of "someValue"? – mevtagezer May 15 '14 at 13:32
  • @mevtagezer volatile is definitely necessary there(if there's no synchronized). And I don't think you need synchronization. If the operation incremented, or in some way used the old value in the calculation of the expression then it would be necessary to synchronize. But I would probably synchronize it anyway. For that you may want to use this though: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html – Cruncher May 15 '14 at 13:38
  • 1
    could you add your last comments to your answer? – mevtagezer May 15 '14 at 13:41
  • @mevtagezer included. Let me know if this is now sufficient information for your question. – Cruncher May 15 '14 at 13:52
  • As long as there is no relationship between `someValue` and another property that needs an update, declaring it `volatile` is enough. (Note that the fact that `BigDecimal` is immutable is important as well here). So you can remove the “I'm not entirely certain” phrase. For the other variables, guarded by `synchronized(timestampLock)`, no `volatile` is required and I recommend removing them to make clear *which* thread-safety construct is the effective one here. But it’s strongly recommended to declare `timestampLock` as `final`. – Holger May 19 '14 at 13:23
1

Nothing new here, this is just a more explicit version of something @Cruncher already said:

You need synchronized whenever it is important for two or more fields in your program to be consistent with one another. Suppose you have two parallel lists, and your code depends on them both being the same length. That's called an invariant as in, the two lists are invariably the same length.

How can you write a method, append(x,y), that adds a new pair of values to the lists without temporarily breaking the invariant? You can't. The method must add one item to the first list, breaking the invariant, and then add the other item to the second list, fixing it again. There's no other way.

In a single-threaded program, that temporary broken state is no problem because no other method can possibly use the lists while append(x,y) is running. That's no longer true in a multithreaded program. In the worst case, append(x,y) could add x to the x list, and then the scheduler could suspend the thread at that exact moment to allow other threads to run. The CPUs could execute millions of instructions before append(x,y) gets to finish the job and make the lists right again. During all of that time, other threads would see the broken invariant, and possibly corrupt your data or crash the program as a result.

The fix is for append(x,y) to be synchronized on some object, and (this is the important part), for every other method that uses the lists to be synchronized on the same object. Since only one thread can be synchronized on a given object at a given time, it will not be possible for any other thread to see the lists in an inconsistent state.

So, if thread A calls append(x,y), and thread B tries to look at the lists "at the same time", will thread B see the what the lists looked like before or after thread A did its work? That's called a data race. And with only the synchronization that I have described so far, there's no way to know which thread will win. All we've done so far is to guarantee one particular invariant.

If it matters which thread wins the race, then that means that there is some higher-level invariant that also needs protection. You will have to add more synchronization to protect that one too. "Thread safety" -- two little words to name a subject that is both broad and deep.

Good Luck, and Have Fun!

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • This is a very good answer. But it is *very* general, and may not be the best answer to *this* question. – Cruncher May 15 '14 at 14:51
  • @Cruncher, It was my answer to mevtagezer's very first sentence, "I am trying to wrap my head around thread safety..." – Solomon Slow May 15 '14 at 15:02
0
    // is synchronisation required?
    public BigDecimal getSomeValue() {
        return someValue;
    }

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

I think Yes you are require to put the synchronization block because consider an example in which one thread is setting the value and at the same time other thread is trying to read from getter method, like here in the example you will see the syncronization block.So, if you take your variable inside the method then you must require the synchronization block.

Community
  • 1
  • 1
Sahil
  • 96
  • 11