PROGRAMMING PARADIGMS - Unit V
PROGRAMMING PARADIGMS - Unit V
Multi Threading:
Java provides built-in support for multithreaded programming. A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate path of execution. A multithreading is a specialized form of multitasking. Multitasking threads require less overhead than multitasking processes. A process consists of the memory space allocated by the operating system that can contain one or more threads. A thread cannot exist on its own; it must be a part of a process. A process remains running until all of the non-daemon threads are done executing. Multithreading enables you to write very efficient programs that make maximum use of the CPU, because idle time can be kept to a minimum.
New: A new thread begins its life cycle in the new state. It remains in this state until the program starts the thread. It is also referred to as a born thread. Runnable: After a newly born thread is started, the thread becomes runnable. A thread in this state is considered to be executing its task.
Waiting: Sometimes a thread transitions to the waiting state while the thread waits for another thread to perform a task.A thread transitions back to the runnable state only when another thread signals the waiting thread to continue executing. Timed waiting: A runnable thread can enter the timed waiting state for a specified interval of time. A thread in this state transitions back to the runnable state when that time interval expires or when the event it is waiting for occurs. Terminated: A runnable thread enters the terminated state when it completes its task or otherwise terminates.
Thread Properties:
Thread Priorities: Every Java thread has a priority that helps the operating system determine the order in which threads are scheduled. Java priorities are in the range between MIN_PRIORITY (a constant of 1) and MAX_PRIORITY (a constant of 10). By default, every thread is given priority NORM_PRIORITY (a constant of 5). Threads with higher priority are more important to a program and should be allocated processor time before lower-priority threads. However, thread priorities cannot guarantee the order in which threads execute and very much platform dependentant. Creating a Thread: Java defines two ways in which this can be accomplished:
You can implement the Runnable interface. You can extend the Thread class, itself.
Create Thread by Implementing Runnable: The easiest way to create a thread is to create a class that implements the Runnable interface. To implement Runnable, a class need only implement a single method called run( ), which is declared like this: public void run( ) You will define the code that constitutes the new thread inside run() method. It is important to understand that run() can call other methods, use other classes, and declare variables, just like the main thread can. After you create a class that implements Runnable, you will instantiate an object of type Thread from within that class. Thread defines several constructors. The one that we will use is shown here:
Thread(Runnable threadOb, String threadName); Here threadOb is an instance of a class that implements the Runnable interface and the name of the new thread is specified by threadName. After the new thread is created, it will not start running until you call its start( ) method, which is declared within Thread. The start( ) method is shown here: void start( ); Example: Here is an example that creates a new thread and starts it running: // Create a new thread. class NewThread implements Runnable { Thread t; NewThread() { // Create a new, second thread t = new Thread(this, "Demo Thread"); System.out.println("Child thread: " + t); t.start(); // Start the thread } // This is the entry point for the second thread. public void run() { try { for(int i = 5; i > 0; i--) { System.out.println("Child Thread: " + i); // Let the thread sleep for a while. Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted."); } System.out.println("Exiting child thread."); } } class ThreadDemo { public static void main(String args[]) { new NewThread(); // create a new thread try { for(int i = 5; i > 0; i--) { System.out.println("Main Thread: " + i); Thread.sleep(1000); }
} catch (InterruptedException e) { System.out.println("Main thread interrupted."); } System.out.println("Main thread exiting."); } } This would produce following result: Child thread: Thread[Demo Thread,5,main] Main Thread: 5 Child Thread: 5 Child Thread: 4 Main Thread: 4 Child Thread: 3 Child Thread: 2 Main Thread: 3 Child Thread: 1 Exiting child thread. Main Thread: 2 Main Thread: 1 Main thread exiting. Create Thread by Extending Thread: The second way to create a thread is to create a new class that extends Thread, and then to create an instance of that class. The extending class must override the run( ) method, which is the entry point for the new thread. It must also call start( ) to begin execution of the new thread. Example: Here is the preceding program rewritten to extend Thread: // Create a second thread by extending Thread class NewThread extends Thread { NewThread() { // Create a new, second thread super("Demo Thread"); System.out.println("Child thread: " + this); start(); // Start the thread } // This is the entry point for the second thread. public void run() { try {
for(int i = 5; i > 0; i--) { System.out.println("Child Thread: " + i); // Let the thread sleep for a while. Thread.sleep(500); } } catch (InterruptedException e) { System.out.println("Child interrupted."); } System.out.println("Exiting child thread."); } } class ExtendThread { public static void main(String args[]) { new NewThread(); // create a new thread try { for(int i = 5; i > 0; i--) { System.out.println("Main Thread: " + i); Thread.sleep(1000); } } catch (InterruptedException e) { System.out.println("Main thread interrupted."); } System.out.println("Main thread exiting."); } } This would produce following result: Child thread: Thread[Demo Thread,5,main] Main Thread: 5 Child Thread: 5 Child Thread: 4 Main Thread: 4 Child Thread: 3 Child Thread: 2 Main Thread: 3 Child Thread: 1 Exiting child thread. Main Thread: 2 Main Thread: 1 Main thread exiting. Thread Methods: Following is the list of important medthods available in the Thread class.
SN 1
Methods with Description public void start() Starts the thread in a separate path of execution, then invokes the run() method on this Thread object. public void run() If this Thread object was instantiated using a separate Runnable target, the run() method is invoked on that Runnable object. public final void setName(String name) Changes the name of the Thread object. There is also a getName() method for retrieving the name. public final void setPriority(int priority) Sets the priority of this Thread object. The possible values are between 1 and 10. public final void setDaemon(boolean on) A parameter of true denotes this Thread as a daemon thread. public final void join(long millisec) The current thread invokes this method on a second thread, causing the current thread to block until the second thread terminates or the specified number of milliseconds passes. public void interrupt() Interrupts this thread, causing it to continue execution if it was blocked for any reason. public final boolean isAlive() Returns true if the thread is alive, which is any time after the thread has been started but before it runs to completion.
4 5
The previous methods are invoked on a particular Thread object. The following methods in the Thread class are static. Invoking one of the static methods performs the operation on the currently running thread SN 1 Methods with Description public static void yield() Causes the currently running thread to yield to any other threads of the same priority that are waiting to be scheduled public static void sleep(long millisec) Causes the currently running thread to block for at least the specified number of milliseconds public static boolean holdsLock(Object x) Returns true if the current thread holds the lock on the given Object. public static Thread currentThread() Returns a reference to the currently running thread, which is the thread that invokes this method. public static void dumpStack()
4 5
Prints the stack trace for the currently running thread, which is useful when debugging a multithreaded application. Example: The following ThreadClassDemo program demonstrates some of these methods of the Thread class: // File Name : DisplayMessage.java // Create a thread to implement Runnable public class DisplayMessage implements Runnable { private String message; public DisplayMessage(String message) { this.message = message; } public void run() { while(true) { System.out.println(message); } } } // File Name : GuessANumber.java // Create a thread to extentd Thread public class GuessANumber extends Thread { private int number; public GuessANumber(int number) { this.number = number; } public void run() { int counter = 0; int guess = 0; do { guess = (int) (Math.random() * 100 + 1); System.out.println(this.getName() + " guesses " + guess); counter++; }while(guess != number); System.out.println("** Correct! " + this.getName() + " in " + counter + " guesses.**");
} } // File Name : ThreadClassDemo.java public class ThreadClassDemo { public static void main(String [] args) { Runnable hello = new DisplayMessage("Hello"); Thread thread1 = new Thread(hello); thread1.setDaemon(true); thread1.setName("hello"); System.out.println("Starting hello thread..."); thread1.start(); Runnable bye = new DisplayMessage("Goodbye"); Thread thread2 = new Thread(hello); thread2.setPriority(Thread.MIN_PRIORITY); thread2.setDaemon(true); System.out.println("Starting goodbye thread..."); thread2.start(); System.out.println("Starting thread3..."); Thread thread3 = new GuessANumber(27); thread3.start(); try { thread3.join(); }catch(InterruptedException e) { System.out.println("Thread interrupted."); } System.out.println("Starting thread4..."); Thread thread4 = new GuessANumber(75); thread4.start(); System.out.println("main() is ending..."); } } This would produce following result. You can try this example again and again and you would get different result every time. Starting hello thread... Starting goodbye thread... Hello Hello Hello
Hello Hello Hello Hello Hello Hello Thread-2 guesses 27 Hello ** Correct! Thread-2 in 102 guesses.** Hello Starting thread4... Hello Hello ..........remaining result produced.
Thread Synchronization : When two or more threads need access to a shared resource, they need some way to ensure that the resource will be used by only one thread at a time. The process by which this synchronization is achieved is called thread synchronization. The synchronized keyword in Java creates a block of code referred to as a critical section. Every Java object with a critical section of code gets a lock associated with the object. To enter a critical section, a thread needs to obtain the corresponding object's lock. This is the general form of the synchronized statement: synchronized(object) { // statements to be synchronized } Here, object is a reference to the object being synchronized. A synchronized block ensures that a call to a method that is a member of object occurs only after the current thread has successfully entered object's monitor. Here is an example, using a synchronized block within the run( ) method: // File Name : Callme.java // This program uses a synchronized block. class Callme { void call(String msg) { System.out.print("[" + msg); try {
Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Interrupted"); } System.out.println("]"); } } // File Name : Caller.java class Caller implements Runnable { String msg; Callme target; Thread t; public Caller(Callme targ, String s) { target = targ; msg = s; t = new Thread(this); t.start(); } // synchronize calls to call() public void run() { synchronized(target) { // synchronized block target.call(msg); } } } // File Name : Synch.java class Synch { public static void main(String args[]) { Callme target = new Callme(); Caller ob1 = new Caller(target, "Hello"); Caller ob2 = new Caller(target, "Synchronized"); Caller ob3 = new Caller(target, "World"); // wait for threads to end try { ob1.t.join(); ob2.t.join(); ob3.t.join(); } catch(InterruptedException e) { System.out.println("Interrupted"); } } } This would produce following result:
[Hello] [World] [Synchronized] Interthread Communication: Consider the classic queuing problem, where one thread is producing some data and another is consuming it. To make the problem more interesting, suppose that the producer has to wait until the consumer is finished before it generates more data. In a polling system, the consumer would waste many CPU cycles while it waited for the producer to produce. Once the producer was finished, it would start polling, wasting more CPU cycles waiting for the consumer to finish, and so on. Clearly, this situation is undesirable. To avoid polling, Java includes an elegant interprocess communication mechanism via the following methods:
wait( ): This method tells the calling thread to give up the monitor and go to sleep until some other thread enters the same monitor and calls notify( ). notify( ): This method wakes up the first thread that called wait( ) on the same object. notifyAll( ): This method wakes up all the threads that called wait( ) on the same object.c The highest priority thread will run first.
These methods are implemented as final methods in Object, so all classes have them. All three methods can be called only from within a synchronized context. These methods are declared within Object. Various forms of wait( ) exist that allow you to specify a period of time to wait. Example: The following sample program consists of four classes: Q, the queue that you're trying to synchronize; Producer, the threaded object that is producing queue entries; Consumer, the threaded object that is consuming queue entries; and PC, the tiny class that creates the single Q, Producer, and Consumer. The proper way to write this program in Java is to use wait( ) and notify( ) to signal in both directions, as shown here: class Q { int n; boolean valueSet = false; synchronized int get() { if(!valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught");
} System.out.println("Got: " + n); valueSet = false; notify(); return n; } synchronized void put(int n) { if(valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } this.n = n; valueSet = true; System.out.println("Put: " + n); notify(); } } class Producer implements Runnable { Q q; Producer(Q q) { this.q = q; new Thread(this, "Producer").start(); } public void run() { int i = 0; while(true) { q.put(i++); } } } class Consumer implements Runnable { Q q; Consumer(Q q) { this.q = q; new Thread(this, "Consumer").start(); } public void run() { while(true) { q.get(); } } } class PCFixed {
public static void main(String args[]) { Q q = new Q(); new Producer(q); new Consumer(q); System.out.println("Press Control-C to stop."); } }
Inside get( ), wait( ) is called. This causes its execution to suspend until the Producer notifies you that some data is ready. When this happens, execution inside get( ) resumes. After the data has been obtained, get( ) calls notify( ). This tells Producer that it is okay to put more data in the queue. Inside put( ), wait( ) suspends execution until the Consumer has removed the item from the queue. When execution resumes, the next item of data is put in the queue, and notify( ) is called. This tells the Consumer that it should now remove it. Here is some output from this program, which shows the clean synchronous behavior: Put: 1 Got: 1 Put: 2 Got: 2 Put: 3 Got: 3 Put: 4 Got: 4 Put: 5 Got: 5 Thread Deadlock: A special type of error that you need to avoid that relates specifically to multitasking is deadlock, which occurs when two threads have a circular dependency on a pair of synchronized objects. For example, suppose one thread enters the monitor on object X and another thread enters the monitor on object Y. If the thread in X tries to call any synchronized method on Y, it will block as expected. However, if the thread in Y, in turn, tries to call any synchronized method on X, the thread waits forever, because to access X, it would have to release its own lock on Y so that the first thread could complete. Example:
To understand deadlock fully, it is useful to see it in action. The next example creates two classes, A and B, with methods foo( ) and bar( ), respectively, which pause briefly before trying to call a method in the other class. The main class, named Deadlock, creates an A and a B instance, and then starts a second thread to set up the deadlock condition. The foo( ) and bar( ) methods use sleep( ) as a way to force the deadlock condition to occur. class A { synchronized void foo(B b) { String name = Thread.currentThread().getName(); System.out.println(name + " entered A.foo"); try { Thread.sleep(1000); } catch(Exception e) { System.out.println("A Interrupted"); } System.out.println(name + " trying to call B.last()"); b.last(); } synchronized void last() { System.out.println("Inside A.last"); } } class B { synchronized void bar(A a) { String name = Thread.currentThread().getName(); System.out.println(name + " entered B.bar"); try { Thread.sleep(1000); } catch(Exception e) { System.out.println("B Interrupted"); } System.out.println(name + " trying to call A.last()"); a.last(); } synchronized void last() { System.out.println("Inside A.last"); } } class Deadlock implements Runnable { A a = new A(); B b = new B(); Deadlock() { Thread.currentThread().setName("MainThread"); Thread t = new Thread(this, "RacingThread"); t.start(); a.foo(b); // get lock on a in this thread. System.out.println("Back in main thread");
} public void run() { b.bar(a); // get lock on b in other thread. System.out.println("Back in other thread"); } public static void main(String args[]) { new Deadlock(); } } Here is some output from this program: MainThread entered A.foo RacingThread entered B.bar MainThread trying to call B.last() RacingThread trying to call A.last() Because the program has deadlocked, you need to press CTRL-C to end the program. You can see a full thread and monitor cache dump by pressing CTRL-BREAK on a PC . You will see that RacingThread owns the monitor on b, while it is waiting for the monitor on a. At the same time, MainThread owns a and is waiting to get b. This program will never complete. As this example illustrates, if your multithreaded program locks up occasionally, deadlock is one of the first conditions that you should check for. Thread Control: While the suspend( ), resume( ), and stop( ) methods defined by Thread class seem to be a perfectly reasonable and convenient approach to managing the execution of threads, they must not be used for new Java programs and obsolete in newer versions of Java. The following example illustrates how the wait( ) and notify( ) methods that are inherited from Object can be used to control the execution of a thread. This example is similar to the program in the previous section. However, the deprecated method calls have been removed. Let us consider the operation of this program. The NewThread class contains a boolean instance variable named suspendFlag, which is used to control the execution of the thread. It is initialized to false by the constructor. The run( ) method contains a synchronized statement block that checks suspendFlag. If that variable is true, the wait( ) method is invoked to suspend the execution of the thread. The mysuspend( ) method sets suspendFlag to true. The myresume( ) method sets suspendFlag to false and invokes notify( ) to wake up the thread. Finally, the main( ) method has been modified to invoke the mysuspend( ) and myresume( ) methods.
Example: // Suspending and resuming a thread for Java 2 class NewThread implements Runnable { String name; // name of thread Thread t; boolean suspendFlag; NewThread(String threadname) { name = threadname; t = new Thread(this, name); System.out.println("New thread: " + t); suspendFlag = false; t.start(); // Start the thread } // This is the entry point for thread. public void run() { try { for(int i = 15; i > 0; i--) { System.out.println(name + ": " + i); Thread.sleep(200); synchronized(this) { while(suspendFlag) { wait(); } } } } catch (InterruptedException e) { System.out.println(name + " interrupted."); } System.out.println(name + " exiting."); } void mysuspend() { suspendFlag = true; } synchronized void myresume() { suspendFlag = false; notify(); } } class SuspendResume { public static void main(String args[]) { NewThread ob1 = new NewThread("One"); NewThread ob2 = new NewThread("Two"); try { Thread.sleep(1000); ob1.mysuspend(); System.out.println("Suspending thread One"); Thread.sleep(1000);
ob1.myresume(); System.out.println("Resuming thread One"); ob2.mysuspend(); System.out.println("Suspending thread Two"); Thread.sleep(1000); ob2.myresume(); System.out.println("Resuming thread Two"); } catch (InterruptedException e) { System.out.println("Main thread Interrupted"); } // wait for threads to finish try { System.out.println("Waiting for threads to finish."); ob1.t.join(); ob2.t.join(); } catch (InterruptedException e) { System.out.println("Main thread Interrupted"); } System.out.println("Main thread exiting."); } } Here is the output produced by the above program: New thread: Thread[One,5,main] One: 15 New thread: Thread[Two,5,main] Two: 15 One: 14 Two: 14 One: 13 Two: 13 One: 12 Two: 12 One: 11 Two: 11 Suspending thread One Two: 10 Two: 9 Two: 8 Two: 7 Two: 6 Resuming thread One Suspending thread Two One: 10 One: 9 One: 8 One: 7
One: 6 Resuming thread Two Waiting for threads to finish. Two: 5 One: 5 Two: 4 One: 4 Two: 3 One: 3 Two: 2 One: 2 Two: 1 One: 1 Two exiting. One exiting. Main thread exiting.
Using isAlive() and join(): Typically, the main thread is the last thread to finish in a program. However, there isnt any guarantee that the main thread wont finish before a child thread finishes. In the previous example, we told the main method to sleep until the child threads terminate. However, we estimated the time it takes for the child threads to complete processing. If our estimate was too short, a child thread could terminate after the main thread terminates. Therefore, the sleep technique isnt the best one to use to guarantee that the main thread terminates last. Programmers use two other techniques to ensure that the main thread is the last thread to terminate. These techniques involve calling the isAlive() method and the join() method. Both of these methods are defined in the Thread class. The isAlive() method determines whether a thread is still running. If it is, the isAlive() method returns a Boolean true value; otherwise, a Boolean false is returned. You can use the isAlive() method to examine whether a child thread continues to run. The join() method works differently than the isAlive() method. The join() method waits until the child thread terminates and joins the main thread. In addition, you can use the join() method to specify the amount of time you want to wait for a child thread to terminate. The following example illustrates how to use the isAlive() method and the join() method in your program. This example is nearly the same as the previous example. The difference lies in the main() method of the Demo class definition. After the threads are declared using the constructor of the MyThread class, the isAlive() method is called for each thread. The value returned by the isAlive() method is then displayed on the screen. Next, the join() method is called for each thread. The join() method causes the main thread to wait for all child threads to complete processing before the main thread terminates. class MyThread implements Runnable {
String tName; Thread t; MyThread (String threadName) { tName = threadName; t = new Thread (this, tName); t.start(); } public void run() { try { System.out.println("Thread: " + tName ); Thread.sleep(2000); } catch (InterruptedException e ) { System.out.println("Exception: Thread " + tName + " interrupted"); } System.out.println("Terminating thread: " + tName ); } } class Demo { public static void main (String args []) { MyThread thread1 = new MyThread ("1"); MyThread thread2 = new MyThread ("2"); MyThread thread3 = new MyThread ("3"); MyThread thread4 = new MyThread ("4"); System.out.println("Thread Status: Alive"); System.out.println("Thread 1: " + thread1.t.isAlive()); System.out.println("Thread 2: " + thread2.t.isAlive()); System.out.println("Thread 3: " + thread3.t.isAlive()); System.out.println("Thread 4: " + thread4.t.isAlive()); try { System.out.println("Threads Joining."); thread1.t.join(); thread2.t.join(); thread3.t.join(); thread4.t.join(); } catch (InterruptedException e) { System.out.println( "Exception: Thread main interrupted."); } System.out.println("Thread Status: Alive"); System.out.println("Thread 1: " + thread1.t.isAlive()); System.out.println("Thread 2: " + thread2.t.isAlive()); System.out.println("Thread 3: "
Here is what is displayed on the screen when this program runs: Thread Status: Alive Thread 1: true Thread 2: true Thread 3: true Thread 4: true Threads Joining. Thread: 1 Thread: 2 Thread: 3 Thread: 4 Terminating thread: 1 Terminating thread: 2 Terminating thread: 3 Terminating thread: 4 Thread Status: Alive Thread 1: false Thread 2: false Thread 3: false Thread 4: false Terminating thread: main thread.
Interrupting Thread: The method is public void interrupt() Interrupts this thread. Unless the current thread is interrupting itself, which is always permitted, the checkAccess method of this thread is invoked, which may cause a SecurityException to be thrown. If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it
will receive an InterruptedException. If this thread is blocked in an I/O operation upon an interruptible channel then the channel will be closed, the thread's interrupt status will be set, and the thread will receive a ClosedByInterruptException. If this thread is blocked in a Selector then the thread's interrupt status will be set and it will return immediately from the selection operation, possibly with a non-zero value, just as if the selector's wakeup method were invoked. If none of the previous conditions hold then this thread's interrupt status will be set. Throws: SecurityException - if the current thread cannot modify this thread public class SleepInterrupt implements Runnable { public void run() { try { System.out.println("in run() - sleep for 20 seconds"); Thread.sleep(20000); System.out.println("in run() - woke up"); } catch (InterruptedException x) { System.out.println("in run() - interrupted while sleeping"); return; } System.out.println("in run() - leaving normally"); } public static void main(String[] args) { SleepInterrupt si = new SleepInterrupt(); Thread t = new Thread(si); t.start(); // Be sure that the new thread gets a chance to // run for a while. try { Thread.sleep(2000); } catch (InterruptedException x) { } System.out.println("in main() - interrupting other thread"); t.interrupt(); System.out.println("in main() - leaving"); } }
Executors:
There's a close connection between the task being done by a new thread, as defined by its Runnable object, and the thread itself, as defined by a Thread object. This works well for small applications, but in large-scale applications, it makes sense to separate thread management and creation from the rest of the application. Objects that encapsulate these functions are known as executors. The following subsections describe executors in detail.
Thread Pools Most of the executor implementations in java.util.concurrent use thread pools, which consist of worker threads. This kind of thread exists separately from the Runnable and Callable tasks it executes and is often used to execute multiple tasks. Using worker threads minimizes the overhead due to thread creation. Thread objects use a significant amount of memory, and in a large-scale application, allocating and deallocating many thread objects creates a significant memory management overhead. One common type of thread pool is the fixed thread pool. This type of pool always has a specified number of threads running; if a thread is somehow terminated while it is still in use, it is automatically replaced with a new thread. Tasks are submitted to the pool via an internal queue, which holds extra tasks whenever there are more active tasks than threads. An important advantage of the fixed thread pool is that applications using it degrade gracefully. To understand this, consider a web server application where each HTTP request is handled by a separate thread. If the application simply creates a new thread for every new HTTP request, and the system receives more requests than it can handle immediately, the application will suddenly stop responding to all requests when the overhead of all those threads exceed the capacity of the system. With a limit on the number of the threads that can be created, the application will not be servicing HTTP requests as quickly as they come in, but it will be servicing them as quickly as the system can sustain. A simple way to create an executor that uses a fixed thread pool is to invoke the newFixedThreadPool factory method in java.util.concurrent.Executors This class also provides the following factory methods:
The newCachedThreadPool method creates an executor with an expandable thread pool. This executor is suitable for applications that launch many shortlived tasks. The newSingleThreadExecutor method creates an executor that executes a single task at a time. Several factory methods are ScheduledExecutorService versions of the above executors.
If none of the executors provided by the above factory methods meet your needs, constructing instances of java.util.concurrent.ThreadPoolExecutor or java.util.concurrent.ScheduledThreadPoolExecutor will give you additional options.
Callables: Callables are a tweak to the existing runnable construct. Callables differ only by having a generic return type - most specialized concurrency APIs already available (such as Foxtrot ) already have the concept of a runnable + return type (such as the foxtrot Task and Job classes). Some of you may already have noticed that the ExecutorService API is biased to Callable objects - and some may consider that a problem since they have a lot of code that already works with Runnable . public class ComplexCalculation implements Callable<Long> { public ComplexCalculation(int value1, int value2, long value3) { // ... } public Long call() { // perform complex calculation resulting in long long result = //... return result; // autoboxing! } }
java.util.concurrent Interface Callable<V> Type Parameters: V - the result type of method call public interface Callable<V> A task that returns a result and may throw an exception. Implementors define a single method with no arguments called call. The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread. A Runnable, however, does not return a result and cannot throw a checked exception. The Executors class contains utility methods to convert from other common forms to Callable classes. V call() Computes a result, or throws an exception if unable to do so. Example:
import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class SimpExec { public static void main(String args[]) { CountDownLatch cdl = new CountDownLatch(5); CountDownLatch cdl2 = new CountDownLatch(5); CountDownLatch cdl3 = new CountDownLatch(5); CountDownLatch cdl4 = new CountDownLatch(5); ExecutorService es = Executors.newFixedThreadPool(2); es.execute(new MyThread(cdl, "A")); es.execute(new MyThread(cdl2, "B")); es.execute(new MyThread(cdl3, "C")); es.execute(new MyThread(cdl4, "D")); try { cdl.await(); cdl2.await(); cdl3.await(); cdl4.await(); } catch (InterruptedException exc) { System.out.println(exc); } es.shutdown(); } } class MyThread implements Runnable { String name; CountDownLatch latch; MyThread(CountDownLatch c, String n) { latch = c; name = n; new Thread(this); } public void run() { for (int i = 0; i < 5; i++) { latch.countDown();
} } } Synchronizers: Java 5 introduces general purpose synchronizer classes, including semaphores, mutexes, barriers, latches, and exchangers, which facilitate coordination between threads. These classes are a apart of the java.util.concurrent package. A brief description of each of these follows: Semaphores A counting semaphore maintains a set of permits. Each acquire() blocks if necessary until a permit is available, and then takes it. Each release() adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly. Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource. Example: import java.util.concurrent.Semaphore; import java.util.Random; //Solving the mutual exclusion problem using Semaphore class class Process2 extends Thread { private static final Random rand = new Random(); private int id; private Semaphore sem; public Process2(int i, Semaphore s) { id = i; sem = s; } private void busy() { try { sleep(rand.nextInt(500)); } catch (InterruptedException e) { } }
private void noncritical() { System.out.println("Thread " + id + " is NON critical"); busy(); } private void critical() { System.out.println("Thread " + id + " entering critical section"); busy(); System.out.println("Thread " + id + " leaving critical section"); } public void run() { for (int i = 0; i < 2; ++i) { noncritical(); try { sem.acquire(); } catch (InterruptedException e) { // ... } critical(); sem.release(); } } public static void main(String[] args) { final int N = 4; System.out.println("Busy waiting..."); //Semaphore(int permits, boolean fair) Semaphore sem = new Semaphore(N, true); Process2[] p = new Process2[N]; for (int i = 0; i < N; i++) { p[i] = new Process2(i, sem); p[i].start(); } } }
Barrier A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized group of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released. For the java.util.concurrent class see CyclicBarrier A barrier is a type of synchronization method. A barrier for a group of threads or processes in the source code means any thread/process must stop at this point and cannot proceed until all other threads/processes reach this barrier.[1] The basic Java synchronization mechanism centers on the keyword synchronized, which predicates methods. In java, all objects can be used like monitors (protected types). That means that given an object A, no more than one thread can execute a synchronized method of A at any time.
Barrier implementation
/* * Given object A, no more than one thread can execute a synchronized method of A at any time. */ public class Barrier { public synchronized void block() throws InterruptedException { wait(); } public synchronized void release() throws InterruptedException { notify(); } public synchronized void releaseAll() throws InterruptedException { notifyAll(); } } And an example with two threads. class BarrierExample {
static class MyThread1 implements Runnable { public MyThread1(Barrier barrier) { this.barrier = barrier; } public void run() { try { Thread.sleep(1000); System.out.println("MyThread1 waiting on barrier"); barrier.block(); System.out.println("MyThread1 has been released"); } catch (InterruptedException ie) { System.out.println(ie); } } private Barrier barrier; } static class MyThread2 implements Runnable { Barrier barrier; public MyThread2(Barrier barrier) { this.barrier = barrier; } public void run() { try { Thread.sleep(3000); System.out.println("MyThread2 releasing blocked threads\n"); barrier.release(); System.out.println("MyThread1 releasing blocked threads\n"); } catch (InterruptedException ie) { System.out.println(ie); } } }
public static void main(String[] args) throws InterruptedException { /* * MyThread1 MyThread2 * ... ... * BR.block(); ... * ... BR.release(); */ Barrier BR = new Barrier(); Thread t1 = new Thread(new BarrierExample.MyThread1(BR)); Thread t2 = new Thread(new BarrierExample.MyThread2(BR)); t1.start(); t2.start(); t1.join(); t2.join(); } } Exchangers A synchronization point at which two threads can exchange objects. Each thread presents some object on entry to the exchange method, and receives the object presented by the other thread on return. Example: import java.util.concurrent.Exchanger; class ExgrDemo { public static void main(String args[]) { Exchanger<String> exgr = new Exchanger<String>(); new UseString(exgr); new MakeString(exgr); } } class MakeString implements Runnable { Exchanger<String> ex; String str; MakeString(Exchanger<String> c) { ex = c; str = new String(); new Thread(this).start(); }
public void run() { char ch = 'A'; for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) str += (char) ch++; try { str = ex.exchange(str); } catch (InterruptedException exc) { System.out.println(exc); } } } } class UseString implements Runnable { Exchanger<String> ex; String str; UseString(Exchanger<String> c) { ex = c; new Thread(this).start(); } public void run() { for (int i = 0; i < 3; i++) { try { str = ex.exchange(new String()); System.out.println("Got: " + str); } catch (InterruptedException exc) { System.out.println(exc); } } } } Latches A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes. A CountDownLatch is initialized with a given count. The await methods block until the current count reaches zero due to invocations of the countDown() method, after which all waiting threads are released and any subsequent invocations of await return immediately.
Latching variables specify conditions that once set never change. This provides a way to start several threads and have them wait until a signal is received from a coordinating thread. [1] The following program creates a set of threads, but doesn't let any thread start until all the threads are created. import java.util.concurrent.*; public class LatchTest { private static final int COUNT = 10; private static class Worker implements Runnable { CountDownLatch startLatch; CountDownLatch stopLatch; String name; Worker(CountDownLatch startLatch, CountDownLatch stopLatch, String name) { this.startLatch = startLatch; this.stopLatch = stopLatch; this.name = name; } public void run() { try { startLatch.await(); // wait until the latch has counted down to // zero } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("Running: " + name); stopLatch.countDown(); } } public static void main(String args[]) { // CountDownLatch(int count) // Constructs a CountDownLatch initialized with the given count. CountDownLatch startSignal = new CountDownLatch(1); CountDownLatch stopSignal = new CountDownLatch(COUNT); for (int i = 0; i < COUNT; i++) {
new Thread(new Worker(startSignal, stopSignal, Integer.toString(i))) .start(); } System.out.println("Go"); startSignal.countDown(); try { stopSignal.await(); } catch (InterruptedException ex) { ex.printStackTrace(); } System.out.println("Done"); } } Thread and event driven programming: The single-thread rule: Here's the rule: Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread. This rule might sound scary, but for many simple programs, you don't have to worry about threads. Before we go into detail about how to write Swing code, let's define two terms: realized and event-dispatching thread. Realized means that the component's paint() method has been or might be called. A Swing component that's a top-level window is realized by having one of these methods invoked on it: setVisible(true), show(), or (this might surprise you) pack(). Once a window is realized, all components that it contains are realized. Another way to realize a component is to add it to a container that's already realized. You'll see examples of realizing components later. The event-dispatching thread is the thread that executes drawing and event-handling code. For example, the paint() and actionPerformed() methods are automatically executed in the event-dispatching thread. Another way to execute code in the event-dispatching thread is to use the SwingUtilities invokeLater() method.
import javax.swing.JPanel; import javax.swing.SwingUtilities; public class InvokeAndWaitDemo extends Object { private static void print(String msg) { String name = Thread.currentThread().getName(); System.out.println(name + ": " + msg); } public static void main(String[] args) { final JLabel label = new JLabel("--------"); JPanel panel = new JPanel(new FlowLayout()); panel.add(label); JFrame f = new JFrame("InvokeAndWaitDemo"); f.setContentPane(panel); f.setSize(300, 100); f.setVisible(true); try { print("sleeping for 3 seconds"); Thread.sleep(3000); print("creating code block for event thread"); Runnable setTextRun = new Runnable() { public void run() { print("about to do setText()"); label.setText("New text!"); } }; print("about to invokeAndWait()"); SwingUtilities.invokeAndWait(setTextRun); print("back from invokeAndWait()"); } catch (InterruptedException ix) { print("interrupted while waiting on invokeAndWait()"); } catch (InvocationTargetException x) { print("exception thrown from run()"); } } }