I'm trying to understand the java.util.concurrent.locks library and wanted to implement two threads which run through a list, whereas the second thread should not overtake (take the lead) the first thread. Specifically, I wanted to implement hand-over-hand locking.
I wrote the following code, which doesn't work. After the two threads ran through the list, the nodes take the value 41 after a certain point. This means the second thread edited them before the first one did. I googled a lot and also had a look at similar question but still couldn't figure it out. I really appreciate your help, thanks!
import java.util.concurrent.locks.ReentrantLock;
class Main {
public static void main(String[] args) throws InterruptedException {
// Generate List
Node first = new Node();
Node current = first;
for(int i = 0; i < 50; i++) {
current.next = new Node();
current = current.next;
}
// run Threads
FirstThread a = new FirstThread(first);
SecondThread b = new SecondThread(first);
a.start();
b.start();
a.join();
b.join();
// Print result
first.print();
}
}
class FirstThread extends Thread {
Node current;
FirstThread(Node start) {
this.current = start;
}
public void run() {
// =================> HAND OVER HAND LOCKING <=================
current.lock.lock();
while(current.next != null) {
current.value = 41;
current.next.lock.lock();
current.lock.unlock();
current = current.next;
}
current.value = 41;
current.lock.unlock();
}
}
class SecondThread extends Thread {
Node current;
SecondThread(Node start) {
current = start;
}
public void run() {
while(current != null) {
current.value++;
current = current.next;
}
}
}
class Node {
ReentrantLock lock;
Node next;
int value;
Node() {
lock = new ReentrantLock();
next = null;
value = 0;
}
public void print() {
Node current = this;
while(current != null) {
System.out.print(current.value + " ");
current = current.next;
}
System.out.println();
}
}
P.S.: I know I should actually insert try and finally blocks if the thread gets interrupted but didn't know where so just neglected that event.
Looks like maybe you don't understand what a Lock
is.
A Lock
object can be in either be owned (a.k.a., locked) by some thread, or it can be available (a.k.a., unlocked).
When a lock l
is not owned by any thread, a call to l.lock()
will change it to be owned by the calling thread, and then it will immediately return; but if l
is owned by some other thread, then the l.lock()
thread will wait (a.k.a., it will block) until the other thread releases its ownership by calling l.unlock()
.
A lock can never be owned by more than one thread, and l.lock()
will not return until the calling thread is the owner.
That's basically all there is to locks.*
Your "first thread" takes and releases ownership of the locks in the list, but your "second thread" completely ignores the locks.
The only thing that would prevent the "second thread" from overtaking the "first thread" is if the second thread was blocked while attempting to own the same lock objects.
Also, you will need some mechanism to prevent the "second thread" from starting down the list before the "first thread" runs. Threads are not guaranteed to run in the same order that you start()
them.
*except for "memory visibility", but that's a whole other subject.