How do the locking object's work with synchronization in java

Viewed 30

I am studying over my university's week 02 content on collaboration and interference for advanced programming.

Firstly, this class is given as an example of an unsynchronized implementation:

class UnsynchronizedCounter implements Counter {
  private int c = 0;
  public void increment() {
    this.c++;
  }
  public void decrement() {
    this.c--;
  }
  public int value() {
    return this.c;
  }
}

Following this it is explained how this can be made thread safe through using synchronization either as a code block or as a method modifier. it is also explained how when synchronizing methods, they need to be given a locking object.

Here is the example solution for a code block approach

class SynchronizedCounter implements Counter {
  private int c = 0;
  public void increment() {
    synchronized(this){
      this.c++;
    }
  }
  public void decrement() {
    synchronized(this){
      this.c--;
    }
  }
  public int value() {
    synchronized(this){
      return this.c;
    }
  }
}

and as you can see the this keyword is used to provide the Counter object as the locking method

Following this is the example using the method modifier approach

class SynchronizedCounterMod implements Counter {
  private int c = 0;
  public synchronized void increment() {
    this.c++;
  }
  public synchronized void decrement() {
    this.c--;
  }
  public synchronized int value() {
    return this.c;
  }
}

However, what I do not understand is in this approach a locking object is not provided at any point, how does this approach know what to use as the locking object?, additionally, assuming this approach assumes the enclosing object is the locking object, does that mean using this approach you cannot provide an alternative locking object?

I tried looking about this online and could find anything I felt explained it properly, so it would be a great help if you could try and help me understand this.

2 Answers

A synchronized function is defined to synchronize on this, so your second example is exactly equivalent to the first.

Note that most modern style guides recommend against synchronizing on this, as other code might accidentally (or intentionally) be using the same object to synchronize, causing locks that block against unintended blocks of code. Instead, it's generally recommended to allocate private objects exclusively used for locking.

class SynchronizedCounter implements Counter {
  private int c = 0;
  private Object lock;
  public SynchronizedCounter() {
    this.lock = new Object();
  }
  public void increment() {
    synchronized(lock){
      this.c++;
    }
  }
  public void decrement() {
    synchronized(lock){
      this.c--;
    }
  }
  public int value() {
    synchronized(lock){
      return this.c;
    }
  }
}

Now no other object can possibly "accidentally" synchronize against this.lock, since it's private and only accessible to this one instance.

Yes, it implicitly uses the enclosing object (this) for synchronization. If you do need to use another object, you'll have to explicitly use synchronized(...) {...} block and pass that object.

Related