Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Java 101: The next generation: Java concurrency without the pain, Part 2

Locking, atomic variables, Fork/Join, and what to expect in Java 8

  • Print
  • Feedback

Page 3 of 7

ReentrantLock or synchronized?

ReentrantLock behaves like synchronized and you might wonder when it's appropriate to use one or the other. Use ReentrantLock when you need timed or interruptible lock waits, non-block-structured locks (obtain a lock in one method; return the lock in another), multiple condition variables, or lock polling. Furthermore, ReentrantLock supports scalability and is useful where there is high contention among threads. If none of these factors come into play, use synchronized.

ReentrantLock declares the following constructors:

  • ReentrantLock() creates a reentrant lock.
  • ReentrantLock(boolean fair) creates a reentrant lock with the given fairness policy. Passing true to fair results in a lock that uses a fair ordering policy, which means that under contention, the lock favors granting access to the longest-waiting thread. The former constructor invokes this constructor, passing false to fair.

ReentrantLock implements Lock's methods: its implementation of unlock() throws java.lang.IllegalMonitorStateException when the calling thread doesn't hold the lock. Additionally, ReentrantLock provides its own methods, including the following trio:

  • int getHoldCount() returns the number of holds on this lock by the current thread: a thread has a hold on a lock for each lock action that isn't matched by an unlock action. When the lock() method is called and the current thread already holds the lock, the hold count is incremented by one and the method returns immediately.
  • boolean isFair() returns the fairness setting.
  • boolean isHeldByCurrentThread() queries if this lock is held by the current thread, returning true when this is the case. This method is often used for debugging and testing.

Listing 1 is a simple demonstration of ReentrantLock.

Listing 1. LockDemo.java

import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

public class LockDemo
{
   public static void main(String[] args)
   {
      ExecutorService executor = Executors.newFixedThreadPool(2);
      final ReentrantLock rl = new ReentrantLock();
 
      class Worker implements Runnable
      {
         private String name;

         Worker(String name)
         {
            this.name = name;
         }

         @Override
         public void run()
         {
           rl.lock();
           try
           {
              if (rl.isHeldByCurrentThread())
                System.out.printf("Thread %s has entered its critical section.%n", 
                                  name);
              System.out.printf("Thread %s is performing work for 2 seconds.%n", name);
              try
              {
                 Thread.sleep(2000);
              }
              catch (InterruptedException ie)
              {
                 ie.printStackTrace();
              }
              System.out.printf("Thread %s has finished working.%n", name);
           }
           finally
           {
              rl.unlock(); 
           }
         }
      }

      executor.execute(new Worker("A"));
      executor.execute(new Worker("B"));

      try
      {
         executor.awaitTermination(5, TimeUnit.SECONDS);
      }
      catch (InterruptedException ie)
      {
         ie.printStackTrace();
      }
      executor.shutdownNow();
   }
}

Listing 1 creates two worker threads. Each thread first acquires a lock to ensure that it has complete access to the critical section. It then outputs some messages and sleeps for two seconds to simulate work. After outputting another message, it releases the lock.

  • Print
  • Feedback

Resources

Previous articles in Java 101: The next generation

Concurrency tutorials on JavaWorld:

  • Modern threading for not-quite-beginners (Cameron Laird, JavaWorld, January 2013): Get an overview of callable and runnable, learn more about synchronized blocks, and find out how you can use java.util.concurrent to work around deadlock and similar threading pitfalls.
  • Multicore processing for client-side Java applications (Kirill Grouchnikov, JavaWorld, September 2007): Get a hands-on introduction to collection sorting using the CountDownLatch and Executors.newFixedThreadPool concurrency utilities.
  • Java concurrency with thread gates (Obi Ezechukwu, JavaWorld, March 2009): See the Java concurrency utilities at work in a realistic implementation of the Thread Gates concurrency pattern.
  • Hyper-threaded Java (Randall Scarberry, JavaWorld, November 2006): See for yourself how two java.util.concurrent classes were used to optimize thread use for faster performance in a real-world application.
  • Java Tip 144: When to use ForkJoinPool vs ExecutorService (Madalin Ilie, JavaWorld, October 2011): Demonstrates the performance impact of replacing the standard ExecutorService class with ForkJoinPool in a web crawler.