1

// this is just an example to better understand. I would like to understand if it is necessary or not to lock I don't know if this example is suitable for what I want to understand .............................................................................................................................................................................................

class Counter{

    int counter;

    public ReentrantLock lock=new ReentrantLock();

    //if the variable is visible to more Threads all readings and writes must be Thread-safe?
    public void incr() {
        lock.lock();
        try {
            counter++;
        } finally {
            lock.unlock();
        }
    }

    public void decr() {
        lock.lock();
        try {
            counter--;
        } finally {
            lock.unlock();
        }
    }

    //must have Lock
    public int getincr() {
        return counter;
    }
}


//these are the Thread
public class Worker extends Thread {

    private Counter c;

    public Worker (Counter c) {
        this.c=c;
    }

    enter code here

    public void run() {

        System.out.println(Thread.currentThread().getName());

        for(int i=1;i<=10;i++) {
            c.incr(); System.out.println(c.getincr());//this is the method
            c.decr(); System.out.println(c.getincr());
        }

        try {
            Thread.currentThread().sleep(500);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


//Main
public class Test {

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        Counter c=new Counter();

        Worker[] threads = new Worker[100];

        for(int i=0;i<100;i++) {
            threads[i]=new Worker(c);
            threads[i].start();
            //threads[i].join();
        }
    }
}
Villat
  • 1,455
  • 1
  • 16
  • 33

1 Answers1

0

Try adding the following two lines to the end of your main method and then adding and removing the locking code.

    Thread.sleep(10000);
    System.out.println("Final counter value: " + c.counter)

What you will see if you run the main method a few times is the following:

  • When you don't have the explicit locking around the incrementing and decrementing of the counter, the end value of the counter might be incorrect.
  • When you have the explicit locking around the counter variable, the final counter value will always be correct (zero) at the end of the test.

An explanation of the above behaviour is that with locking, the value of the counter that is being accessed is always correct. Since both increment and decrement use the same explicit lock object before operating on the counter variable, the JVM guarantees that you will have the latest value of the variable.

Regarding the getincr() in the Counter class, since the value that it accesses and returns might also at the same time be incremented or decremented by another thread (than the one that is doing the reading), you need to acquire a lock in order to ensure that you are reading the latest value of the counter. To quote the authoritative book Concurrency in Practice (section 2.4. Guarding State with Locks):

It is a common mistake to assume that synchronization needs to be used only when writing to shared variables; this is simply not true. For each mutable state variable that may be accessed by more than one thread, all accesses to that variable must be performed with the same lock held. In this case, we say that the variable is guarded by that lock.

Another way of implementing your locking could have been to use a ReentrantReadWriteLock, in which case the Counter class methods incr() and decr() would acquire write locks and the getincr() method would acquire a read lock.

Additionally, since only one operation is performed in each of the Counter methods incr(), decr() and getincr(), you could substitute locking with the use of an AtomicInteger for your counter variable (instead of using an int), which would also provide thread-safe access.

Thomas Kabassis
  • 1,326
  • 1
  • 12
  • 17
  • Yes I understand what you said what I can't understand when the getincr () method must have the Lock – user12182425 Oct 08 '19 at 19:02
  • See the following: https://codeday.me/en/qa/20190305/615.html Essentially: Threads can have local copies of variables, and locking is is what forces the local values to be updated between threads. – Thomas Bitonti Oct 08 '19 at 21:32
  • 1
    This is better: https://stackoverflow.com/questions/11459543/should-getters-and-setters-be-synchronized – Thomas Bitonti Oct 08 '19 at 21:34
  • @ThomasBitonti The Concurrency in Practice quote in the SO question you mention is exactly the quote I was looking for! – Thomas Kabassis Oct 08 '19 at 22:02
  • tomkab Thank you very much . I have done several tests to verify that not putting the Lock () in a Get () method changes something but I can't find it. Does anyone have an example to show that something changes? – user12182425 Oct 08 '19 at 22:19