Domanda

I am learning about synchronized methods as a means of preventing race conditions and unwanted behavior in Java. I was presented with the following example, and told the race condition is quite subtle:

public class Messages {
  private String message = null;
  private int count = 2;

  // invariant 0 <= count && count <= 2

  public synchronized void put(String message) {
    while( count < 2 )
      this.wait();
    this.message = message;
    this.count = 0; 
    this.notifyAll(); 
  }

  public synchronized String getMessage() {
    while( this.count == 2 )
      this.wait();
    String result = this.message;
    this.count += 1;
    this.notifyAll();
    return result; 
  }
}

Subtle or not, I think I have a fundamental misunderstanding of what synchronized methods do. I was under the impression they restrict access to threads through use of a lock token (or similar), and thus can never race. How, then, does this example have a race condition, if its methods are synchronized? Can anyone help clarify?

È stato utile?

Soluzione 2

Think about ways that count could become > 2...

That code has a bad smell, too. What is count supposed to be counting? Why does get increment it and put reset it? Why the unnecessary use of 'this'? If I saw code like that in a project, I would look at it very carefully...

Altri suggerimenti

I presume that what the author had in mind is that, since count goes from 0 to 2, two threads might call put() in sequence, and the reader threads would thus miss one of the messages.

It's indeed a race condition: readers and putters compete for the same lock, and if the messages being read depends on which thread is notified by notifyAll().

Multi threading is when you use new Thread(runnable).start(); this starts a new thread and goes to the run() method. The runnable is any class that implements runnable. Or extends thread a synchronized method makes sure that if these threads want to read data changed by the synchronized method it will be possible, otherwise it might be unchanged, or worse, half-changed.

Java's synchronized methods buys you mutual exclusion between the two methods, which means that you can assume they will not interleave.

However, you still have a race condition because you can get different behavior depending on which method runs first.

As JB Nizet suggested in his answer, consider what happens with each of the two orderings (assume they are running in different threads).

A race condition occurs whenever two entities compete for a single resource, which can cause unpredictable behavior if the outcome depends on the order. When you use notifyAll() all threads are woken up and they race to obtain the lock they were waiting for, and it's impossible to say which will execute next.

I don't think having a count value >2 is the problem if the code can work as expected. Since both put() and getMessage() methods are synchronized both method's can't be called at the same time. So if an thread calls getMessage() and has the count value 2, another thread can't call the put() method to set the count = 0 and notify the waiting thread.There is too much synchronizing, which cause an deadlock. So the while part shouldn't be synchronized and can solved like this.

 public void put(String message) {
    while( count < 2 )
      this.wait();
   synchronzied(this){
    this.message = message;
    this.count = 0; 
    this.notifyAll(); 
 }

 }

  public String getMessage() {
    while( this.count == 2 )
      this.wait();
   
    synchronized(this){
    String result = this.message;
    this.count += 1;
    this.notifyAll();
    
 }
return result;
 }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top