13

When Synchronization is used there is a performance impact. Can volatile be used in combination with synchronized to reduce the performance overhead ? For example, instance of Counter will be shared among many threads and each thread can access Counter's public methods. In the below code volatile is used for getter and synchronized is used for setter

public class Counter
{
    private volatile int count;

    public Counter()
    {
        count = 0;
    }

    public int getCount()
    {
        return count;
    }

    public synchronized void increment()
    {
        ++count;
    }   
}

Please let me know in which scenario this might break ?

Arne
  • 2,106
  • 12
  • 9
Karthik
  • 345
  • 3
  • 11
  • I'd say no. Cause the two aren't related (in the way you seem to think). Besides, in you code, you've all ready obtained the monitor lock when you enter the method. – MadProgrammer Jul 27 '12 at 04:14
  • Yes lock is obtained for setter but for getter its not obtained. – Karthik Jul 27 '12 at 04:21
  • But you're not modifying count in the getter, this only ensures that the value is checked every time and not cached – MadProgrammer Jul 27 '12 at 04:30
  • It would work in this case, but if your synchronized write method were doing multiple operations on the state such that the state gets updated but inconsistent during such operations, then this won't work. E.g. if you did count++ twice in increment(), getCount() may return the first increment and then the next increment on subsequent calls, which may be incorrect for your business logic. – shrini1000 Oct 16 '12 at 08:20

4 Answers4

12

Yes, you definitely can. In fact, if you look at the source code of AtomicInteger, it's essentially what they do. AtomicInteger.get simply returns value, which is a volatile int (link). The only real difference from what you've done and what they do is that they use a CAS for the increment instead of synchronization. On modern hardware, a CAS can eliminate any mutual exclusion; on older hardware, the JVM will put some sort of mutex around the increment.

Volatile reads are about as fast as non-volatile ones, so the reads will be quite fast.

Not only that, but volatile fields are guaranteed not to tear: see JLS 17.7, which specifies that volatile longs and doubles are not subject to word tearing. So your code would work with a long just as well as an int.

As Diego Frehner points out, you might not see the result of an increment if you get the value "right as" the increment happens -- you'll either see the before or the after. Of course, if get were synchronized you'd have exactly the same behavior from the read thread -- you'd either see the before-increment or post-increment value. So it's really the same either way. In other words, it doesn't make sense to say that you won't see the value as it's happening -- unless you meant word tearing, which (a) you won't get and (b) you would never want.

yshavit
  • 42,327
  • 7
  • 87
  • 124
  • Thanks for the details. There is really no difference from synchronized getter to an AtomicInteger, because synchronized does not guarantee fifo access. We should add, that if you never have to read an old value (ex. increase bank account) then u need another solution. – Diego Frehner Jul 27 '12 at 05:50
  • If volatile reads are "about as fast" as non-volatile ones, why wouldn't you go directly to main memory for everything? – Tyson Jul 27 '12 at 17:45
  • (a) Because they're about as fast, not exactly as fast, and (b) because volatile writes tend to be slower. This is a platform-specific thing, though. I believe on most machines out there, the write will take longer, but the reads tend to be pretty cheap. I think if you know there hasn't been a write to the volatile since the last time you read it from main memory to your cache, then you can still use the cache version -- not positive about that, though. – yshavit Jul 27 '12 at 19:33
  • Also, see: http://stackoverflow.com/questions/1090311/are-volatile-variable-reads-as-fast-as-normal-reads – yshavit Jul 27 '12 at 19:35
7

1. I have personally used this mechanism of volatile combined with synchronized.

2. You can alone use synchronized, and you will always get a consistent result, but using only volatile alone will Not yield the same result always.

3. This is because volatile keyword is not a synchronization primitive. It merely prevents caching of the value on the thread, but it does not prevent two threads from modifying the same value and writing it back concurrently.

4. volatile give concurrent access to threads without lock, but then using synchronized will allow only one thread to get access to this and all the synchronized methods in the class.

5. And using both volatile and synchronized will do this....

volatile - will reflect the changed values to thread, and prevent caching,

synchronized - But using synchronized keyword, will make sure that only one thread gets the access to the synchronized methods of the class.

Kumar Vivek Mitra
  • 33,294
  • 6
  • 48
  • 75
  • It should be highlighted that the purpose of the `synchronized` in this context is to allow only one thread to perform the read/write (increment) combination; access is just a bit too general a term. – Richard Sitze Jul 27 '12 at 05:15
  • @ Richard is think u missed the 4th point, where i have highlighted that `only one thread to get access to this and all the synchronized methods in the class.` – Kumar Vivek Mitra Jul 27 '12 at 05:26
  • @ Kumar, no actually I was speaking directly to that forth point, and specifically that phrase. I thought you could strengthen it. "gets the access" might suggest "get" or "read access" - a bit ambiguous? No? I thought you were very clear and concise for the most part, so I did up-vote your response earlier. – Richard Sitze Jul 27 '12 at 05:59
  • In synchronization, its complete read and write access on that objects synchronized methods, whose lock has been obtained by that thread. – Kumar Vivek Mitra Jul 27 '12 at 06:05
0

You will not always get the most actual count when calling getCount(). An AtomicInteger could be appropriate for you.

Diego Frehner
  • 2,396
  • 1
  • 28
  • 35
  • Could you please let me know the scenario when would I not get the latest value. – Karthik Jul 27 '12 at 04:22
  • When Thread A is calling getCount() while Thread B is trying to get the exclusive lock. – Diego Frehner Jul 27 '12 at 04:28
  • Maybe i should add while Thread C has the exclusive increment lock. I mean in the described way you don't have a guaranteed read write order, but you always get the last written result. It depends on your use case what you need. – Diego Frehner Jul 27 '12 at 04:49
  • My scenario is when any of the writer threads W1,W2...Wn increments the counter, it should be immediately reflected to the reader threads R1,R2...Rn. In this case will the above code wouldn't be an efficient solution than using synchronized in getter too ? Please let me know your thoughts. – Karthik Jul 27 '12 at 05:08
  • If you only need the most actual written value than the above solution or AtomicInteger works. yshavits answer is explaining quite well :) – Diego Frehner Jul 27 '12 at 05:56
0

There wouldn't be a performance gain from using both. Volatile guarantees that the value of a variable will be consistent when reading/writing to the variable across threads executing in parallel by preventing caching. Synchronized, when applied to a method (as you do in your example), only allows a single thread to enter that method at a time and blocks others until execution is complete.

Tyson
  • 1,685
  • 15
  • 36
  • 1
    When I was reading about Volatile, what I understood was, it doesn't support mutual exclusive lock. It only assures that all threads will read volatile data from main memory and won't cache it in any level. And any thread updating the volatile data will immediately be reflected in main memory. Please check this [link](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4) – Karthik Jul 27 '12 at 04:50
  • 1
    Volatiles don't require any sort of global lock. – yshavit Jul 27 '12 at 05:07
  • You're correct, the article on volatile that I read/linked was bad so I removed it, read up a bit more on volatile, and updated the text in my answer. Thanks for the heads up. – Tyson Jul 27 '12 at 06:27