1

I am trying to learn threading, and regarding the following example

public class LockExample {
    private Lock lockone = new ReentrantLock();

    public void method1() {
        lockone.lock();
        try {
            // do something
        } finally {
            lockone.unlock();
        } 
    }

    public void method2() {
        lockone.lock();
        try {
            // do something
        } finally {
            lockone.unlock();
        }
    }
}
  1. does it mean that if we lock method1 and method2 using the same lock, say thread A and B can not access method1 or method2at the same time. But if we lock method1 and method2using different locks lockone and locktwo, then threadA can access method1, at the same time thread Bcan access method2?

  2. why don't we lock each method separately instead of putting them in one lock?

 public class LockExample {
     private Lock lockone = new ReentrantLock();

         public void method1() {
             lockone.lock();
             try {
                 // do something
             } // wrap the two methods in one lock? 
         }

         public void method2() {

             try {
                 // do something
             } finally {
                 lockone.unlock();
             }
         }
     }
}
3
  • The second fragment of code you posted does not make sense. For example, if a thread calls method2 without holding the lock (i.e. without having previously called method1) then IllegalMonitorStateException is thrown. Commented Oct 18, 2014 at 10:26
  • @CristianGreco - I think the OP is trying to explain his logic rather then the actual code:) Commented Oct 18, 2014 at 10:27
  • Maybe this is obvious to you, but just in case it's not: Each instance of your LockExample class has its own separate ReentrantLock object. So if you have two LockExample objects, a and b, it is possible for one thread to be in a.method1() while a different thread is in b.method2(). Again, sorry if it's obvious, but it seems to be an idea that a lot of beginners don't easily grasp. Commented Oct 18, 2014 at 15:25

3 Answers 3

1

does it mean that if we lock method1 and method2 using the same lock, say thread A and B can not access method1 or method2at the same time. But if we lock method1 and method2using different locks lockone and locktwo, then threadA can access method1, at the same time thread Bcan access method2?

Yes, if method1 and method2 using the same lock, then thread A and B cannot access method1 or method 2 at same time. But if methods using different locks, then thread A and B will not be able to access same methods, but accessing different methods will work. That is, thread A and B can't access same method1, or same method2. But while thread A accessing method1, thread B can access method2.

why don't we lock each method separately instead of putting them in one lock?

If you want any threads to block method 2 from accessing, till first thread has not finished access / executtion of method1 and method2, then given code sample is correct.

Example:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Main implements Runnable {
    private Lock lock = new ReentrantLock();

    public void method1() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + " entered method1.");

        Thread.sleep(1000);

        lock.lock();

        System.out.println(Thread.currentThread().getName() + " ackquired lock.");

        Thread.sleep(1000);
    }

    public void method2() {
        System.out.println(Thread.currentThread().getName() + " entered method2.");

        lock.unlock();

        System.out.println(Thread.currentThread().getName() + " released lock.");
    }

    public void run() {
        try{
            method1();
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            method2();
        }
    }

    public static void main(String [] args) {
        Runnable runnable = new Main();

        new Thread(runnable, "ThreadA").start();
        new Thread(runnable, "ThreadB").start();
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

In this case can any threads call method2 only, with out calling method1?
no. in second case, any thread must first invoke method1 and then method2. Else IllegalMonitorException will be thrown.
0

If you use the same lock for both methods, then if thread-1 is executing method-1 (i.e, after acquiring the lock), then no other thread can execute method-1 as well as method-2.

If you use 2 different locks for 2 methods, then if thread-1 and thread-2 are executing method-1 and method-2 by acquiring lock on lock-1 and lock-2 respectively, then other threads can execute method-1 if thread-1 releases the lock and method-2 if thread-2 releases the lock.

2 Comments

Thanks! But why don't we, or can we make method1 and 2 into one critical section by only acquiring the lock in methos1, and releasing the lock after method2? Sorry I can't see what's wrong with this...
@stillAFanOfTheSimpsons - No. We can't do it. Synchronization establishes a happens-before relationship. Deep down, when the JVM will be executing instructions, it can re-order instructions within a synchronized block or outside a synchronized block (or method) but never between synchronized blocks, to improve performance.
0

why don't we, or can we make method1 and 2 into one critical section by only acquiring the lock in methos1, and releasing the lock after method2?

Because it's bad design.

The office where I work has hundreds of thousands of lines of Java code to maintain. Some of it's been around for as long as ten years, and developers have been coming and going and working in that code all that time. Adherence to strict style rules and strict conventions is an important part of how we keep it all safe and sane and mostly bug-free.

If we use a ReentrantLock, we use always use it just how you used it in your first example:

lockone.lock();
try {
    // do something
} finally {
    lockone.unlock();
} 

If there's a methodOne, and a methodTwo, and there's a need to call them both atomically, then we write it like this:

private methodOne(...) { ... }
private methodTow(...) { ... }
public callMethodOneAndMethodTwo(...) {
    lockOneTwo.lock();
    try {
        methodOne(...);
        methodTwo(...);
    } finally {
        lockOneTwo.unlock();
    }

This way of managing the lock guarantees that no thread can ever fail to unlock the lock after it's done what it needs to do. It guarantees that no other function in any other package can call methodOne() without subsequently calling methodTwo(). It guarantees that no such function can call methodTwo() without first calling methodOne(). It's easier to understand, and it's easier to maintain.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.