5 things you didn't know about ... java.util.concurrent, Part 2

Concurrent programming means working smarter, not harder

In addition to concurrency-friendly Collections, java.util.concurrent introduced other pre-built components that can assist you in regulating and executing threads in multithreaded applications. Ted Neward introduces five more of his Java™ programming must-haves from the java.util.concurrent package.

Share:

Ted Neward, Principal, Neward & Associates

Ted Neward photoTed Neward is the principal of Neward & Associates, where he consults, mentors, teaches, and presents on Java, .NET, XML Services, and other platforms. He resides near Seattle, Washington.



01 June 2010

Also available in Chinese Russian Japanese Vietnamese Portuguese

Concurrent Collections make concurrent programming easier by providing thread-safe, well-tuned data structures. In some cases, however, developers need to go one step further, and think about regulating and/or throttling thread execution. Given that the whole point of java.util.concurrent is to simplify multithreaded programming, you might hope the package would include synchronization utilities — and it does.

This article, a follow-up to Part 1, introduces several synchronization constructs that are a higher level than the core language primitives (monitors), but not so high that they're buried inside a Collection class. Using these locks and gates is fairly straightforward once you know what they're for.

About this series

So you think you know about Java programming? The fact is, most developers scratch the surface of the Java platform, learning just enough to get the job done. In this series, Ted Neward digs beneath the core functionality of the Java platform to uncover little-known facts that could help you solve even the stickiest programming challenges.

1. Semaphore

In some enterprise systems, it's not uncommon for developers to need to throttle the number of open requests (threads/actions) against a particular resource — in fact, throttling can sometimes improve the throughput of a system by reducing the amount of contention against that particular resource. While it's certainly possible to try to write the throttling code by hand, it's easier to use the semaphore class, which takes care of throttling for you, as shown in Listing 1:

Listing 1. Use Semaphore to throttle
import java.util.*;import java.util.concurrent.*;

public class SemApp
{
    public static void main(String[] args)
    {
        Runnable limitedCall = new Runnable() {
            final Random rand = new Random();
            final Semaphore available = new Semaphore(3);
            int count = 0;
            public void run()
            {
                int time = rand.nextInt(15);
                int num = count++;
                
                try
                {
                    available.acquire();
                    
                    System.out.println("Executing " + 
                        "long-running action for " + 
                        time + " seconds... #" + num);
                
                    Thread.sleep(time * 1000);

                    System.out.println("Done with #" + 
                        num + "!");

                    available.release();
                }
                catch (InterruptedException intEx)
                {
                    intEx.printStackTrace();
                }
            }
        };
        
        for (int i=0; i<10; i++)
            new Thread(limitedCall).start();
    }
}

Even though the 10 threads in this sample are running (which you can verify by executing jstack against the Java process running SemApp), only three are active. The other seven are held at bay until one of the semaphore counts is released. (Actually, the Semaphore class supports acquiring and releasing more than one permit at a time, but that wouldn't make sense in this scenario.)


2. CountDownLatch

Develop skills on this topic

This content is part of progressive knowledge paths for advancing your skills. See:

If Semaphore is the concurrency class designed to allow threads "in" one at a time (perhaps evoking memories of bouncers at popular nightclubs), then CountDownLatch is the starting gate at a horse race. This class holds all threads at bay until a particular condition is met, at which point it releases them all at once.

Listing 2. CountDownLatch: Let's go to the races!
import java.util.*;
import java.util.concurrent.*;

class Race
{
    private Random rand = new Random();
    
    private int distance = rand.nextInt(250);
    private CountDownLatch start;
    private CountDownLatch finish;
    
    private List<String> horses = new ArrayList<String>();
    
    public Race(String... names)
    {
        this.horses.addAll(Arrays.asList(names));
    }
    
    public void run()
        throws InterruptedException
    {
        System.out.println("And the horses are stepping up to the gate...");
        final CountDownLatch start = new CountDownLatch(1);
        final CountDownLatch finish = new CountDownLatch(horses.size());
        final List<String> places = 
            Collections.synchronizedList(new ArrayList<String>());
        
        for (final String h : horses)
        {
            new Thread(new Runnable() {
                public void run() {
                    try
                    {
                        System.out.println(h + 
                            " stepping up to the gate...");
                        start.await();
                        
                        int traveled = 0;
                        while (traveled < distance)
                        {
                            // In a 0-2 second period of time....
                            Thread.sleep(rand.nextInt(3) * 1000);
                            
                            // ... a horse travels 0-14 lengths
                            traveled += rand.nextInt(15);
                            System.out.println(h + 
                                " advanced to " + traveled + "!");
                        }
                        finish.countDown();
                        System.out.println(h + 
                            " crossed the finish!");
                        places.add(h);
                    }
                    catch (InterruptedException intEx)
                    {
                        System.out.println("ABORTING RACE!!!");
                        intEx.printStackTrace();
                    }
                }
            }).start();
        }

        System.out.println("And... they're off!");
        start.countDown();        

        finish.await();
        System.out.println("And we have our winners!");
        System.out.println(places.get(0) + " took the gold...");
        System.out.println(places.get(1) + " got the silver...");
        System.out.println("and " + places.get(2) + " took home the bronze.");
    }
}

public class CDLApp
{
    public static void main(String[] args)
        throws InterruptedException, java.io.IOException
    {
        System.out.println("Prepping...");
        
        Race r = new Race(
            "Beverly Takes a Bath",
            "RockerHorse",
            "Phineas",
            "Ferb",
            "Tin Cup",
            "I'm Faster Than a Monkey",
            "Glue Factory Reject"
            );
        
        System.out.println("It's a race of " + r.getDistance() + " lengths");
        
        System.out.println("Press Enter to run the race....");
        System.in.read();
        
        r.run();
    }
}

Notice in Listing 2 that CountDownLatch serves two purposes: First it releases all of the threads simultaneously, simulating the start of the race; but later a different latch simulates the end of the race, essentially so that the "main" thread can print out the results. For a race with more commentary, you could add CountDownLatches at the "turns" and "halfway" points of the race, as horses crossed the quarter, half, and three-quarter values in the distance.


3. Executor

The examples in Listing 1 and Listing 2 both suffer from a fairly frustrating flaw, in that they force you to create Thread objects directly. This is a recipe for trouble because in some JVMs, creating a Thread is a heavyweight operation, and it's far better to reuse existing Threads than to create new ones. In other JVMs, however, it's exactly the opposite: Threads are pretty lightweight, and it's far better to just new-up one when you need it. Of course, if Murphy has his way (which he usually does), whichever approach you use will be exactly the wrong one for the platform you end up deploying on.

The JSR-166 expert group (see Resources) anticipated this situation, to some degree. Rather than have Java developers create Threads directly, they introduced the Executor interface, an abstraction for making new threads. As shown in Listing 3, Executor allows you to create threads without having to new the Thread objects yourself:

Listing 3. Executor
Executor exec = getAnExecutorFromSomeplace();
exec.execute(new Runnable() { ... });

The main drawback to using Executor is the same one we encounter with all factories: the factory has to come from someplace. Unfortunately, unlike the CLR, the JVM doesn't ship with a standard VM-wide thread pool.

The Executor class does serve as a common place to get Executor-implementing instances, but it only has new methods (to create a new thread pool, for example); it doesn't have precreated instances. So you are on your own if you want to create and use Executor instances throughout your code. (Or, in some cases, you will be able to use an instance provided by your container/platform of choice.)

ExecutorService, at your service

As useful as it is to not have to worry about where Threads come from, the Executor interface lacks some functionality that a Java developer might expect, such as the ability to kick off a thread designed to produce a result and wait in a non-blocking fashion until that result becomes available. (This is a common need in desktop applications, where a user will execute a UI operation that requires accessing a database, and yet could want to cancel the operation before it completes if it takes too long.)

For this, the JSR-166 experts created a far more useful abstraction, the ExecutorService interface, which models the thread-starting factory as a service that can be controlled collectively. For example, rather than calling execute() once for each task, the ExecutorService could take a collection of tasks and return a List of Futures representing the future results of each of those tasks.


4. ScheduledExecutorServices

As great as the ExecutorService interface is, certain tasks need to be done in a scheduled fashion, such as executing a given task at determined intervals or at a specific time. This is the province of the ScheduledExecutorService, which extends ExecutorService.

If your goal was to create a "heartbeat" command that "pinged" every five seconds, ScheduledExecutorService would make it as simple as what you see in Listing 4:

Listing 4. ScheduledExecutorService 'pings' on schedule
import java.util.concurrent.*;

public class Ping
{
    public static void main(String[] args)
    {
        ScheduledExecutorService ses =
            Executors.newScheduledThreadPool(1);
        Runnable pinger = new Runnable() {
            public void run() {
                System.out.println("PING!");
            }
        };
        ses.scheduleAtFixedRate(pinger, 5, 5, TimeUnit.SECONDS);
    }
}

How about that? No fussing with threads, no fussing with what to do if the user wants to cancel the heartbeat, no explicitly marking threads as foreground or background; just leave all those scheduling details to ScheduledExecutorService.

Incidentally, if a user did want to cancel the heartbeat, the return from the scheduleAtFixedRate call would be a ScheduledFuture instance, which not only wraps around the result if there is one, but also has a cancel method to shut down the scheduled operation.


5. Timeout methods

The ability to put a concrete timeout around blocking operations (and thus avoid deadlocks) is one of the great advances of the java.util.concurrent library over its older concurrency cousins, such as monitors for locking.

These methods are almost always overloaded with an int/TimeUnit pair, indicating how long the method should wait before bailing out and returning control to the program. It requires more work on the part of the developer — how will you recover if the lock isn't acquired? — but the results are almost always more correct: fewer deadlocks and more production-safe code. (For more about writing production-ready code, see Michael Nygard's Release It! in Resources.)


In conclusion

The java.util.concurrent package contains many more nifty utilities that extend well beyond Collections, particularly in the .locks and .atomic packages. Dig in and you'll also find useful control structures like CyclicBarrier and more.

Like many aspects of the Java platform, you don't need to look very hard to find infrastructure code that can be tremendously helpful. Whenever you're writing multithreaded code, remember the utilities discussed in this and the previous article.

Next time, we'll branch out into a new topic: five things you didn't know about Jars.


Download

DescriptionNameSize
Sample code for this article5things5-src.zip10KB

Resources

Learn

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Java technology on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=493553
ArticleTitle=5 things you didn't know about ... java.util.concurrent, Part 2
publish-date=06012010