Marko Kitonjics
Marko Kitonjics

Reputation: 257

How to use intrinsic locks in java?

I'm reading the official Java Tutorial provided by Oracle and I wanted to put my knowledge into practice.

I wanted to see Thread Interference in action and solving it using Intrinsic Locks and Synchronization. So I created a class called Counter with:

.

public class Apple {
    public static void main(String[] args) {
        Counter myCounter = new Counter();
        Thread a = new Thread(myCounter);
        Thread b = new Thread(myCounter);
     
        a.start();
        b.start();
    }
}

class Counter implements Runnable {
    public int a = 0;
    public int b = 0;

    void incA() {
        ++a;
    }

    void decA() {
        --a;
    }

    void incB() {
        ++b;
    }

    void decB() {
        --b;
    }

    void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            incA();
            decA();
            incB();
            decB();
            printValues();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

First I run my program without using intrinsic locks or synchronization and the output was what I expected, thread interference.

...
a: 0 | b: 0
a: 0 | b: 1
a: 0 | b: 0
a: 1 | b: 0
a: 0 | b: 0
...

Now I wanted to sort this problem using intrinsic locks so that while one thread is incrementing or decrementing a, the other thread can change b at the same time, rather than using synchronized methods which would prevent that.

So I added two new fields (locks) and synchronized blocks using the intrinsic locks. Here's the new code:

class Counter implements Runnable {
    public int a = 0;
    public int b = 0;
    Object lock1 = new Object();
    Object lock2 = new Object();

    void incA() {
        synchronized (lock1) {
            ++a;
        }
    }

    void decA() {
        synchronized (lock1) {
            --a;
        }
    }

    void incB() {
        synchronized (lock2) {
            ++b;
        }
    }

    void decB() {
        synchronized (lock2) {
            --b;
        }
    }

    void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            incA();
            decA();
            incB();
            decB();
            printValues();
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

I haven't made any change to the main method so I didn't include it. But I ran this code three times and this was the output on the last try:

...
a: 0 | b: 0
a: 0 | b: 0
a: 0 | b: 1
a: 0 | b: 0

What did I do wrong? How should the code look like?

I just want a simple example code that uses intrinsic locks.

Upvotes: 3

Views: 270

Answers (2)

theo
theo

Reputation: 995

How about you remove printValues() from run() method and in your main thread you wait for all thread to finish before printing? So main would be:

public static void main(String[] args) throws InterruptedException {
 Counter myCounter = new Counter();
 Thread a = new Thread(myCounter);
 Thread b = new Thread(myCounter);

 a.start();
 b.start();

 a.join();
 b.join();
 myCounter.printValues();
}

Upvotes: 1

JB Nizet
JB Nizet

Reputation: 691735

printValues() reads and prints the values while another thread increments or decrements them: it's not synchronized. And even if it was, it could read and print b between the calls to incB() and decB() of the other thread.

So you can have

  • thread 1 increments b --> b = 1
  • thread 2 reads and prints b --> b is printed as 1
  • thread 1 decrements b --> b = 0

If nobody is ever supposed to see b different from 0, then incB();decB() should be a single atomic operation, by putting these two calls in a single synchronized block, and the read of b should also be put into a synchronized block, using the same lock:

class Counter implements Runnable {
    private int a = 0;
    private int b = 0;
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    private void incA() {
        ++a;
    }

    private void decA() {
        --a;
    }

    private void incB() {
        ++b;
    }

    private void decB() {
        --b;
    }

    private void printValues() {
        System.out.println("a: " + a + " | b: " + b);
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (lock1) {
                incA();
                decA();
            }

            synchronized (lock2) {
                incB();
                decB();
            }

            synchronized (lock1) {
                synchronized (lock2) {
                    printValues();
                }
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Upvotes: 4

Related Questions