Java explicit lock example

17 November 2013
By Gonçalo Marques
In this tutorial we will implement an explicit lock in Java using the synchronized statement together with the wait/notify idiom.

Introduction

Before Java 5 - where the java.util.concurrent package concurrency facilities were introduced - we had to implement locking mechanisms from scratch. Even now when Java already offers a robust locking infra-structure one could need some very specific locking strategy that may be only possible if implemented from scratch.

In this tutorial we will implement a lock in Java resorting to the synchronized statement together with the wait/notify idiom. The locking mechanism we are going to implement is also feasible using the locking infra-structure that was included in Java 5, so it will be mostly for educational purposes and as an example to other explicit locking mechanisms you may require.

For more information on the synchronized modifier please refer to Java synchronized example.

The lock usage

We are going to implement a lock that may be used to synchronize access to some shared resource like a database. It could be used to access any other sensitive component or section of your application.

The lock will provide a couple of methods:

Lock implementation methods

public boolean lock(int timeout){

  // Try to acquire the lock.
  // If timeout is reached before
  // acquiring the lock we should give up

}

public void releaseLock() {

  // Release the previously acquired lock

}

Using the methods described above, an example usage of the lock could look like the following:

Example lock usage

Lock lock = LockProvider.getDatabaseLock();
boolean lockAcquired = lock.lock(2000);
if (lockAcquired) {
  try {
    doWorkWithTheDatabase();
  } finally {
    lock.releaseLock();
  }
} else {
  throw new RuntimeException("Could not acquire lock!");
}

Method doWorkWithTheDatabase will be executed by a single thread at a given time after the thread acquires the lock.

The lock implementation

Following next is a possible lock implementation (actually it is a reentrant lock i.e. a thread may acquire the same lock for an arbitrary number of times but then it must release it for the same number of times it previously acquired it):

Lock implementation

package com.byteslounge.concurrency;

import java.util.concurrent.TimeUnit;

public class Lock {

  private final Object lockObject = new Object();
  private Thread lockingThread;
  private int lockCount = 0;

  public boolean lock(int timeout) {

    synchronized (lockObject) {

      if (lockingThread != null
          && lockingThread != Thread.currentThread()) {

        try {

          long elapsedTime = 0L;
          long startWaitingTime;

          while (lockingThread != null) {

            startWaitingTime = TimeUnit.NANOSECONDS.toMillis(System
                .nanoTime());
            lockObject.wait(timeout);
            elapsedTime += TimeUnit.NANOSECONDS.toMillis(System
                .nanoTime()) - startWaitingTime;

            if (timeout > 0 && elapsedTime > timeout) {
              return false;
            }

          }

        } catch (InterruptedException e) {
          return false;
        }
      }

      lockCount++;
      if (lockingThread == null) {
        lockingThread = Thread.currentThread();
      }

      return true;
    }

  }

  public void releaseLock() {

    synchronized (lockObject) {

      if (lockingThread != Thread.currentThread()) {
        throw new IllegalStateException(
            "Only lock holding thread may release the lock!");
      }

      lockCount--;
      if (lockCount == 0) {
        lockingThread = null;
        lockObject.notify();
      }

    }

  }

}

When we try to acquire the lock through lock method we check if there is no other thread already holding the lock (lockingThread != null) and if the lock holding thread is not already the current thread (lockingThread != Thread.currentThread()).

It the holding thread is already the current thread we increment the lock counter. We are implementing a reentrant lock i.e. a thread may acquire the same lock an arbitrary number of times. In order to release the lock the thread must release it the same number of times it previously acquired it.

When the lock is already acquired by another thread we enter the waiting loop. Threads will hold execution in the wait statement until the waiting timeout expires or in case of a notification from releaseLock method occurs.

For each waiting condition loop iteration we check if the timeout has already expired. If the timeout did expire we give up of waiting for the lock.

Note: Remember that all the code in both lock and releaseLock methods is synchronized in lockObject's intrinsic lock so only a single thread will ever execute code in any of both code blocks at a given time.

Note 2: When a thread waiting in the wait instruction is awaken by the notify instruction in releaseLock method it will only proceed after all threads exit any other lockObject synchronized sections, including the thread that just invoked notify instruction (it's inside a lockObject synchronized code section).

Testing

Let's do some testing of our lock implementation:

Lock testing

package com.byteslounge.concurrency;

public class Test {

  public static void main(String[] args) throws InterruptedException {

    Lock lock = new Lock();
    MyThread[] threadArray = new MyThread[10];

    for (int i = 0; i < 10; i++) {
      threadArray[i] = new MyThread(lock);
    }
    for (int i = 0; i < 10; i++) {
      threadArray[i].start();
    }
    for (int i = 0; i < 10; i++) {
      threadArray[i].join();
    }
  }

  static class MyThread extends Thread {

    private final Lock lock;

    public MyThread(Lock lock) {
      this.lock = lock;
    }

    @Override
    public void run() {
      boolean lockAcquired = lock.lock(2000);
      if (lockAcquired) {
        System.out
            .println("Thread " + this.getId() + ": Lock acquired");
        try {
          System.out.println("Thread " + this.getId() + ": Working");
        } finally {
          lock.releaseLock();
          System.out.println("Thread " + this.getId()
              + ": Lock released");
        }
      } else {
        throw new RuntimeException("Could not acquire lock!");
      }
    }

  }

}

The following output is the result of a single execution of our test class:

Thread 8: Lock acquired
Thread 8: Working
Thread 8: Lock released
Thread 9: Lock acquired
Thread 9: Working
Thread 9: Lock released
Thread 10: Lock acquired
Thread 10: Working
Thread 10: Lock released
Thread 11: Lock acquired
Thread 11: Working
Thread 11: Lock released
Thread 12: Lock acquired
Thread 12: Working
Thread 12: Lock released
Thread 13: Lock acquired
Thread 13: Working
Thread 13: Lock released
Thread 14: Lock acquired
Thread 14: Working
Thread 14: Lock released
Thread 17: Lock acquired
Thread 17: Working
Thread 17: Lock released
Thread 15: Lock acquired
Thread 15: Working
Thread 15: Lock released
Thread 16: Lock acquired
Thread 16: Working
Thread 16: Lock released

The lock implementation source code is available for download at the end of this page.

Download source code from this article

Related Articles

Comments

About the author
Gonçalo Marques is a Software Engineer with several years of experience in software development and architecture definition. During this period his main focus was delivering software solutions in banking, telecommunications and governmental areas. He created the Bytes Lounge website with one ultimate goal: share his knowledge with the software development community. His main area of expertise is Java and open source.

GitHub profile: https://github.com/gonmarques

He is also the author of the WiFi File Browser Android application: