Sunday, June 15, 2014

Locks

Locks allow restricting access to different areas of the code. A part of the code that we want only one thread to have access at a given time is called a critical section. This critical section can represent access to some resource (like a printer; we don't want another process to start printing in the middle), execution of a particular function or part within the program (like an ATM transaction, there should be no interference while it occurs), or a method of a distributed data type (avoid erasing while inserting an element to a list).

Let's see the following code (without locks)

public class Printer implements Runnable {

private String document;

public Printer(String document){
this.document = document;
}

@Override
public void run() {
Random r = new Random();
//represents some preprocessing of the document
try {
Thread.sleep(1000*(r.nextInt(2)+1));
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
for (int i=0; i < document.length(); i++){
System.out.println("printing \"" + document.charAt(i) + "\"");
try {
Thread.sleep(1000*(r.nextInt(2)+1));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

public static void main(String[] args) {
Thread t1 = new Thread(new Printer("hello"));
Thread t2 = new Thread(new Printer("world"));
t1.start();
t2.start();
}

}

The main method starts two thread printing "hello" and "world" respectively. The run method of the Printer class (invoked when the threads are started), prints each letter and has some delay (imagine the physical time to actually print the letter. Then, when running this program the printing of both documents gets winded and the output letters do not form "hello" "world", but some mix of their letters.

One way to deal with this is by using locks within the for loop so that from the moment the printer starts it cannot get interrupted: (changes to the run method)

@Override
public void run() {
Random r = new Random();
//represents some preprocessing of the document
try {
Thread.sleep(1000*(r.nextInt(2)+1));
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (true){
boolean success = lock.tryLock();
if (success) break;
try {
Thread.sleep(1000*(r.nextInt(2)+1));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int i=0; i < document.length(); i++){
System.out.println("printing \"" + document.charAt(i) + "\"");
try {
Thread.sleep(1000*(r.nextInt(2)+1));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
lock.unlock();
}

Now, only one thread can access the for loop at a time. 


Question to think: what would have happened had we forgotten the unlock after the loop?



PS: The goal of this post was to give a first explanation of locks (without entering into language details) Java allows using synchronized for avoiding multiple threads from entering the same method. For a discussion on the difference between locks and synchronize, there's a nice explanation here: locks vs synchronized

No comments:

Post a Comment