鄭无爲
鄭无爲

Reputation: 53

Confused java ThreadPool and ReentrantLock

I get a fixed number of two threads, then submitted 100 tasks, inside which I

used a lock and intentionally leave it unlocked, running result of this code is

sorted number from 1 to 99, this makes me confused:

1) Is it because the thread is reused so that the same thread can acquire it multiple times?

2) If so, lock does not block thread, it still can be reused? What the lock guards is only the lines within its scope.

Please correct me.

public class LockTest {

    public static volatile int a = 1;

    static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        for (int k = 0; k < 100; k++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    lock.lock();
                    System.out.println(a++);
                }
            });
        }
        executorService.shutdown();
    }
}

Upvotes: 0

Views: 834

Answers (2)

Harmlezz
Harmlezz

Reputation: 8068

If you modify your code this way you get all the information required to answer your questions by yourself:

public static void main(String[] args) {
    ExecutorService executorService = newFixedThreadPool(2);
    for (int k = 0; k < 100; k++) {
        executorService.submit(() -> {
            lock.lock();
            System.out.println(currentThread().getId() +
                             " hold count: " + lock.getHoldCount());
            System.out.println("a = " + a++);
        });
    }
    executorService.shutdown();
}

Output Sample

12 hold count: 1
a = 1
12 hold count: 2
a = 2
12 hold count: 3
a = 3
...
12 hold count: 98
a = 98
12 hold count: 99
a = 99

As you can see:

  • you want to output 100 numbers but you do output only 99.
  • the output shows that only one thread is working, because you never release the lock the first thread obtained. The second thread wait for obtaining the lock.
  • because you use a ReentrantLock the first thread which obtained the lock may continue working, because he already owns the lock.
  • the JVM will never shutdown, because the first thread will never release the lock and hence the second thread waits forever.

Answer for question asked by PO in comment

It is kind of predictable: the CachedThreadPool will create new Threads on the fly as required. What happens is:

  • the first thread will obtain the lock forever (because it never releases it). It will process submitted tasks as fast as it can.
  • while the first thread is processing, the CachedThreadPool will create new threads to execute the remaining tasks submitted but not yet processed.
  • depending on the speed of the first thread, which obtained the lock, processing submitted tasks which are not already scheduled to other threads, you will end up with many threads, waiting forever.

Upvotes: 2

Sidias-Korrado
Sidias-Korrado

Reputation: 403

I would say your analysis is correct.

  1. Yes, the first thread that aquires the lock holds it. The second thread blocks at the lock call.
  2. Yes , the point of ReentrantLock is that the same thread can lock it multiple times. The lock guards not lines, but the locking call itself.

If you want that other threads have the ability to acquire the lock, then each lock() call must have a corresponding unlock() call.

Upvotes: 0

Related Questions