Reputation: 257
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
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
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
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