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 1

Get started with the Java Concurrency Utilities

  • Print
  • Feedback

Page 2 of 6

  • java.util.concurrent contains high-level utility types that are commonly used in concurrent programming. Examples include semaphores, barriers, thread pools, and concurrent hashmaps.
    • The java.util.concurrent.atomic subpackage contains low-level utility classes that support lock-free thread-safe programming on single variables.
    • The java.util.concurrent.locks subpackage contains low-level utility types for locking and waiting for conditions, which are different from using Java's low-level synchronization and monitors.

The Java Concurrency Utilities framework also exposes the low-level compare-and-swap (CAS) hardware instruction, variants of which are commonly supported by modern processors. CAS is much more lightweight than Java's monitor-based synchronization mechanism and is used to implement some highly scalable concurrent classes. The CAS-based java.util.concurrent.locks.ReentrantLock class, for instance, is more performant than the equivalent monitor-based synchronized primitive. ReentrantLock offers more control over locking. (In Part 2 I'll explain more about how CAS works in java.util.concurrent.)

System.nanoTime()

The Java Concurrency Utilities framework includes long nanoTime(), which is a member of the java.lang.System class. This method enables access to a nanosecond-granularity time source for making relative time measurements.

In the next sections I'll introduce three useful features of the Java Concurrency Utilities, first explaining why they're so important to modern concurrency and then demonstrating how they work to increase the speed, reliability, efficiency, and scalability of concurrent Java applications.

The Executor framework

In threading, a task is a unit of work. One problem with low-level threading in Java is that task submission is tightly coupled with a task-execution policy, as demonstrated by Listing 1.

Listing 1. Server.java (Version 1)

import java.io.IOException;

import java.net.ServerSocket;
import java.net.Socket;

class Server
{
   public static void main(String[] args) throws IOException
   {
      ServerSocket socket = new ServerSocket(9000);
      while (true)
      {
         final Socket s = socket.accept();
         Runnable r = new Runnable()
                      {
                         @Override
                         public void run()
                         {
                            doWork(s);
                         }
                      };
         new Thread(r).start();
      }
   }

   static void doWork(Socket s)
   {
   }
}

The above code describes a simple server application (with doWork(Socket) left empty for brevity). The server thread repeatedly calls socket.accept() to wait for an incoming request, and then starts a thread to service this request when it arrives.

Because this application creates a new thread for each request, it doesn't scale well when faced with a huge number of requests. For example, each created thread requires memory, and too many threads may exhaust the available memory, forcing the application to terminate.

You could solve this problem by changing the task-execution policy. Rather than always creating a new thread, you could use a thread pool, in which a fixed number of threads would service incoming tasks. You would have to rewrite the application to make this change, however.

  • Print
  • Feedback

Resources

Using the Java Concurrency Utilities -- more 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.

Popular articles in the Java 101 series