views:

128

answers:

5

If I have several mutable properties in an object that will be acted upon by several threads, I understand they should be synchronized.

class Doggie {
    private String name;
    private int    age;

    public void setName(String name) { this.name = name; }
    public String getName() { return this.name; }
    public void setAge(int age) { this.age = age; }
    public int getAge() { return this.age; }

}

Questions:

  • Are not return and assignment atomic operations in Java?
  • Since properties might not necessarily be interrelated, it does not always make sense to synchronize with the same lock. How to organize the locking structure?
  • Is it better to go with the intrinsic lock or a private Object lock pattern?
+2  A: 

Operations with references are atomic, but not volatile - you will always see the old value or the new value, but there's no guarantee you'll see the new value without some sort of memory barrier. I can't remember the details of which primitives are guaranteed to be atomic - probably all but long and double.

Personally I'd use a single private lock until I saw any evidence that it was a bottleneck. I would advise against locking on "this" as other code might lock on it too. If you're the only code that knows about the lock, it's harder to get interference. Having said that, if callers want to atomically change more than one property, you may want to expose the lock via a property.

Do you definitely need a threadsafe mutable type? If you could avoid that requirement it would make life simpler.

Jon Skeet
@dcp Mind you I'm slower than normal as this answer and comment are being typed on a phone.
Jon Skeet
A: 
  • They are atomic operations, but picture a scenario where two clients are trying to get and set a piece of data at the same time. There is no guarantee as to which order things are going to be called which could greatly affect the results of your application. (The classic example is money transactions.)
  • It may or may not make sense to synchronize with the same lock - that really depends on your application. However, it typically is not a good idea to lock the entire object out if it is not necessary.
  • As with what Jon said, start with a single, private lock and go from there depending on results.
JasCav
A: 

You're right to take note that non-interrelated properties can have different locks. Considering that locking objects require trivial memory, I would personally go with a lock per property instead of one for the entire object.

The lightweight way to do this is just to have a boolean that's set while the property is being written to and clear otherwise. The heavyweight way to do this, to support timeouts etc., is with a mutex.

Reinderien
Java mutexes don't support timeouts.
Stephen C
Ah, that's a shame..
Reinderien
Well, the "inbuilt" mutexes you get with `synchronized` don't. If you want timeouts, you can always explicitly use a `java.util.concurrent.locks.ReentrantLock`, which has `tryLock()` methods both with and without timeouts.
Andrzej Doyle
+5  A: 

Your example begs for an immutable object. http://java.sun.com/docs/books/tutorial/essential/concurrency/imstrat.html

Romain Hippeau
+2  A: 
  • Are not return and assignment atomic operations in Java?

Yes they are atomic, but atomicity is not the only issue. The key issue is whether the action of a write to an attribute is guaranteed to be visible to a following read for the same attribute.

  • When the reads and writes are in the same thread, the read is guaranteed to see the earlier write.

  • When the reads and writes are in different threads, the read is only guaranteed to see the earlier write if the two threads synchronize properly ... or if the attribute is declared as volatile.

Note that primitive locks/mutexes are not the only way to synchronize.

  • Since properties might not necessarily be interrelated, it does not always make sense to synchronize with the same lock. How to organize the locking structure?

It makes sense to use multiple locks if (and only if) lock contention is likely. In your example, lock contention is only likely to be an issue if some Doggie instance receives a very high rate of get and/or set operations.

  • Is it better to go with the intrinsic lock or a private Object lock pattern?

It depends. If your application is going use the Doggie object's primitive lock, then you might get lock contention or even unintended locking out of get and set operations. In that case a private lock might be advisable. Otherwise, a private lock is an unnecessary overhead.

Stephen C