Which of the following methods will start the thread public class Mythread implements runnable?
IntroductionJava supports single-thread as well as multi-thread operations. A single-thread program has a single entry point (the main() method) and a single exit point. A multi-thread program has an initial entry point (the main() method), followed by many entry and exit points, which are run concurrently with the main(). The term "concurrency" refers to doing multiple tasks at the same time. Show
Java has built-in support for concurrent programming by running multiple threads concurrently within a single program. A thread, also called a lightweight process, is a single sequential flow of programming operations, with a definite beginning and an end. During the lifetime of the thread, there is only a single point of execution. A thread by itself is not a program because it cannot run on its own. Instead, it runs within a program. The following figure shows a program with 3 threads running under a single CPU: Multitasking (or Multi-processing)Modern operating systems (such as Windows and UNIX) are multitasking system. A multitasking system can perform many tasks concurrently by sharing the computing resources, such as CPU(s), main memory, and I/O channels. In a single-CPU machine, only one task can be executed at one time – through time-slicing of the CPU. In a multi-CPU machine, a few tasks can be executed simultaneously, either distributed among or time-slicing the CPUs. Multitasking is necessary in today's operating systems for better performance by making full use and optimize the usage of the computing resources. There are generally two kinds of multitasking operating systems:
Multithreading (within a Process)In UNIX, we fork a new process. In Windows, we start a program. A process or program has its own address space and control blocks. It is called heavyweight because it consumes a lot of system resources. Within a process or program, we can run multiple threads concurrently to improve the performance. Threads, unlike heavyweight process, are lightweight and run inside a single process – they share the same address space, the resources allocated and the environment of that process. It is lightweight because it runs within the context of a heavyweight process and takes advantage of the resources allocated for that program and the program’s environment. A thread must carve out its own resources within the running process. For example, a thread has its own stack, registers and program counter. The code running within the thread works only within that context, hence, a thread (of a sequential flow of operations) is also called an execution context. Multithreading within a program improves the performance of the program by optimizing the usage of system resources. For example, while one thread is blocked (e.g., waiting for completion of an I/O operation), another thread can use the CPU time to perform computations, resulted in better performance and overall throughput. Multithreading is also necessary to provide better interactivity with the users. For example, in a word processor, while one thread is printing or saving the file, another thread can be used to continue typing. In GUI applications, multithreading is essential in providing a responsive user interface. For this article, I shall assume that you understand Swing programming, as Swing applications rely on multithreading (to perform their specific function, repaint and process the events) and best to illustrate multithreading. A typical Java program runs in a single process, and is not interested in multiple processes. However, within the process, it often uses multiple threads to to run multiple tasks concurrently. A standalone Java application starts with a single thread (called main thread) associated with the main() method. This main thread can then start new user threads. The Infamous "Unresponsive User Interface"The infamous Unresponsive User Interface (UI) problem is best illustrated by the following Swing program with a counting-loop. The GUI program has two buttons. Pushing the "Start Counting" button starts the counting. Pushing the "Stop Counting" button is supposed to stop (pause) the counting. The two button-handlers communicate via a boolean flag called stop. The stop-button handler sets the stop flag; while the start-button handler checks if stop flag has been set before continuing the next count. You should write the program under Eclipse/NetBeans so that we could trace the threads. Example 1: Unresponsive UI
However, once the START button is pushed, the UI is frozen – the counter value is not updated on the display (i.e., the display is not refreshed), and the user interface is not responding to the clicking of the STOP button, or any other user interaction. Tracing the threads (Advanced)From the program trace (via Eclipse/NetBeans), we observe:
It is recommended to run the GUI construction codes on the EDT via the invokeLater(). This is because many of the GUI components are not guaranteed to be thread-safe. Channeling all accesses to GUI components in a single thread ensure thread safety. Suppose that we run the constructor directly on the main() method (under the "main" thread), as follow: public static void main(String[] args) { new UnresponsiveUI(); }The trace shows that:
In the earlier case, the EDT is started via the invokeLater(); while in the later case, the EDT starts after setVisible(). Example 2: Still Unresponsive UI with ThreadInstead of using the event-dispatching thread to do the compute-intensive counting, let's create a new thread to do the counting, instead of using the EDT, as follows:
A new thread is created by sub-classing the Thread class, with an anonymous inner class. We override the run() method to specify the running behavior of the thread, which performs the compute-intensive counting. An instance is created. Invoking the start() method of the instance causes the run() to execute on its own thread. (The details on creating new thread will be explained later.) The responsiveness improves slightly. But the proper counter value is still not shown, and there is a delay in response to the "STOP" button. (You may not see the difference running with a dual-core processor.) This is because the counting thread does not voluntarily yield control to the EDT. The "starved" EDT is unable to update the display and response to the "STOP" button. Nonetheless, the JVM may force the counting thread to yield control according to the scheduling algorithm, which results in delay on updating the display (TODO: not sure about this). Tracing the Threads (Advanced)When the "START" button is clicked, a new thread called "Thread-n" (n is a running number) is created to run the compute-intensive counting-loop. However, this thread is not programmed to yield control to other threads, in particular, the event-dispatching thread. This program is, however, slightly better than the previous program. The display is updated, and the clicking of "STOP" button has its effect after some delays. Example 3: Responsive UI with ThreadLet's modify the program by making a call to the counting-thread's sleep() method, which requests the counting-thread to yield control to the event-dispatching thread to update the display and response to the "STOP" button. The counting program now works as desired. The sleep() method also provides the necessary delay needed.
The sleep() method suspends the current thread and put it into the waiting state for the specified number of milliseconds. Another thread can begin execution (in a single CPU environment). (The sleep() can be interrupted by invoking the interrupt() method of this thread, which triggers an InterruptedException - this is unusual!) In this case, the thread created to do the counting ("Thread-n") yields control voluntarily to other threads after every count (via the "sleep(10)"). This allows the event-dispatching thread to refresh the display as well as processing the "STOP" button after each count. Example 4: SwingWorkerJDK 1.6 provides a new javax.swing.SwingWorker class, which can be used to run compute-intensive tasks in background threads, and passes the final result or intermediate results back to methods that run on the event-dispatching thread. We shall discuss SwingWorker in the later section. Creating a new ThreadThere are two ways to create a new thread:
The second method is needed as Java does not support multiple inheritance. If a class already extends from a certain superclass, it cannot extend from Thread, and have to implement the Runnable interface. The second method is also used to provide compatibility with JDK 1.1. It should be noted that the Thread class itself implements the Runnable interface. The run() method specifies the running behavior of the thread and gives the thread something to do. You do not invoke the run() method directly from your program. Instead, you create a Thread instance and invoke the start() method. The start() method, in turn, will call back the run() on a new thread. Interface RunnableThe interface java.lang.Runnable declares one abstract method run(), which is used to specify the running behavior of the thread: Class ThreadThe class java.lang.Thread has the following constructors: public Thread(); public Thread(String threadName); public Thread(Runnable target); public Thread(Runnable target, String threadName);The first two constructors are used for creating a thread by sub-classing the Thread class. The next two constructors are used for creating a thread with an instance of class that implements Runnable interface. The class Thread implements Runnable interface, as shown in the class diagram. As mentioned, the method run() specifies the running behavior of the thread. You do not invoke the run() method explicitly. Instead, you call the start() method of the class Thread. If a thread is constructed by extending the Thread class, the method start() will call back the overridden run() method in the extended class. On the other hand, if a thread is constructed by providing a Runnable object to the Thread's constructor, the start() method will call back the run() method of the Runnable object (and not the Thread's version). Creating a new Thread by sub-classing Thread and overriding run()To create and run a new thread by extending Thread class:
For example, class MyThread extends Thread { @Override public void run() { } ...... }public class Client { public static void main(String[] args) { ...... MyThread t1 = new MyThread(); t1.start(); ...... new MyThread().start(); ...... } }Often, an inner class (named or anonymous) is used instead of a ordinary subclass. This is done for readability and for providing access to the private variables and methods of the outer class. For example, public class Client { ...... public Client() { Thread t = new Thread() { @Override public void run() { } }; t.start(); ... new MyThread().start(); } class MyThread extends Thread { public void run() { } } }Example
A class called MyThead is created by extending Thread class and overriding the run() method. A constructor is defined to takes a String as the name of the thread. The run() method prints 1 to 5, but invokes yield() to yield control to other threads voluntarily after printing each number.
The test class allocates and starts three threads. The output is as follows: Thread 1: 1 Thread 3: 1 Thread 1: 2 Thread 2: 1 Thread 1: 3 Thread 3: 2 Thread 2: 2 Thread 3: 3 Thread 1: 4 Thread 1: 5 Thread 3: 4 Thread 3: 5 Thread 2: 3 Thread 2: 4 Thread 2: 5Take note that the output is indeterminate (different run is likely to produce different output), as we do not have complete control on how the threads would be executed. Creating a new Thread by implementing the Runnable InterfaceTo create and run a new thread by implementing Runnable interface:
Again, an inner class (named or anonymous) is often used for readability and to provide access to the private variables and methods of the outer class. Thread t = new Thread(new Runnable() { public void run() { } }); t.start();Methods in the Thread ClassThe methods available in Thread class include:
The stop(), suspend(), and resume() methods have been deprecated in JDK 1.4, because they are not thread-safe, due to the release of monitors. See JDK API documentation for more discussion. Daemon threadsThere are two kinds of threads, daemon threads and user threads. A daemon thread can be set via the setDaemon(boolean on) method. A daemon thread is an infrastructure thread, e.g., the garbage collector thread and the GUI's event dispatcher thread. The JVM exits when the only threads running are all daemon threads. In other words, the JVM considers its job done, when there is no more user threads and all the remaining threads are its infrastructure threads. [@PENDING more] The Life Cycle of a ThreadThe thread is in the "new" state, once it is constructed. In this state, it is merely an object in the heap, without any system resources allocated for execution. From the "new" state, the only thing you can do is to invoke the start() method, which puts the thread into the "runnable" state. Calling any method besides the start() will trigger an IllegalThreadStateException. The start() method allocates the system resources necessary to execute the thread, schedules the thread to be run, and calls back the run() once it is scheduled. This put the thread into the "runnable" state. However, most computers have a single CPU and time-slice the CPU to support multithreading. Hence, in the "runnable" state, the thread may be running or waiting for its turn of the CPU time. A thread cannot be started twice, which triggers a runtime IllegalThreadStateException. The thread enters the "not-runnable" state when one of these events occurs:
For the "non-runnable" state, the thread becomes "runnable" again:
A thread is in a "terminated" state, only when the run() method terminates naturally and exits. The method isAlive() can be used to test whether the thread is alive. The isAlive() returns false if the thread is "new" or "terminated". It returns true if the thread is "runnable" or "not-runnable". JDK 1.5 introduces a new getState() method. This method returns an (nested) enum of type Thread.State, which takes a constant of {NEW, BLOCKED, RUNNABLE, TERMINATED, WAITING}.
Thread Scheduling and PriorityJVM implements a fixed priority thread-scheduling scheme. Each thread is assigned a priority number (between the Thread.MIN_PRIORITY and Thread.MAX_PRIORITY). The higher the number, the higher is the priority for the thread. When a new thread is created, it inherits the priority number from the thread that created it. You can used the method setPriority() to change the priority number of a thread as follows: public void setPriority(int priority);The int priority is JVM dependent. It may take a value between 1 (lowest priority) to 10. JVM chooses the highest-priority thread for execution. If there is more than one thread with the same highest-priority, JVM schedules them in a round-robin manner. JVM also implements a preemptive scheduling scheme. In a preemptive environment, if at any time a higher priority thread becomes "runnable", the current lower priority thread will yield control to the higher priority thread immediately. If there are more than one equal-priority runnable threads, one thread may run until the completion without yielding control to other equal-priority threads. This is known as starvation. Therefore, it is a good practice to yield control to other equal-priority thread via the sleep() or yield() method. However, you can never yield control to a lower-priority thread. In some operating systems such as Windows, each of the running thread is given a specific amount of CPU time. It is known as time slicing to prevent a thread from starving the other equal-priority threads. However, do not rely on time slicing, as it is implementation dependent. Hence, a running thread will continue running until:
An important point to note is the thread scheduling and priority is JVM dependent. This is natural as JVM is a virtual machine and requires the native operating system resources to support multithreading. Most JVM does not guarantee that the highest-priority thread is being run at all times. It may choose to dispatch a lower-priority thread for some reasons such as to prevent starvation. Therefore, you should not rely on the priority in your algorithm. Monitor Lock & SynchronizationA monitor is an object that can be used to block and revive thread. It is supported in the java.lang.Object root class, via these mechanisms:
Each Java object has a lock. At any time, the lock is controlled by, at most, a single thread. You could mark a method or a block of the codes with keyword synchronized. A thread that wants to execute an object's synchronized code must first attempt to acquire its lock. If the lock is under the control of another thread, then the attempting thread goes into the Seeking Lock state and becomes ready only when the lock becomes available. When a thread that owns a lock completes the synchronized code, it gives up the lock. Keyword "synchronized"For example, public synchronized void methodA() { ...... } public void methodB() { synchronized(this) { ...... } synchronized(anObject) { ...... } ...... }Synchronization can be controlled at method level or block level. Variables cannot be synchronized. You need to synchronized the ALL the methods that access the variables. private static int counter = 0; public static synchronized void increment() { ++counter; } public static synchronized void decrement() { --counter; }You can also synchronized on static methods. In this case, the class lock (instead of the instance lock) needs to be acquired in order to execute the method. Example
It is important to note that while the object is locked, synchronized methods and codes are blocked. However, non-synchronized methods can proceed without acquiring the lock. Hence, it is necessary to synchronize all the methods involved the shared resources. For example, if synchronized access to a variable is desired, all the methods to that variable should be synchronized. Otherwise, a non-synchronized method can proceed without first obtaining the lock, which may corrupt the state of the variable. wait(), notify() & notifyAll() for Inter-Thread SynchronizationThese methods are defined in the java.lang.Object class (instead of java.land.Thread class). These methods can only be called in the synchronous codes. The wait() and notify() methods provide a way for a shared object to pause a thread when it becomes unavailable to that thread and to allow the thread to continue when appropriate. Example: Consumer and ProducerIn this example, a producer produces a message (via putMessage() method) that is to be consumed by the consumer (via getMessage() method), before it can produce the next message. In a so-called producer-consumer pattern, one thread can suspend itself using wait() (and release the lock) until such time when another thread awaken it using notify() or notifyAll().
The output messages (on System.out) may appear out-of-order. But closer inspection on the put/get timestamp confirms the correct sequence of operations. The synchronized producer method putMessage() acquires the lock of this object, check if the previous message has been cleared. Otherwise, it calls wait(), releases the lock of this object, goes into WAITING state and places this thread on this object's "wait" set. On the other hand, the synchronized consumer's method getMessage() acquires the lock of this object and checks for new message. If there is a new message, it clears the message and issues notify(), which arbitrarily picks a thread on this object's "wait" set (which happens to be the producer thread in this case) and place it on BLOCKED state. The consumer thread, in turn, goes into the WAITING state and placed itself in the "wait" set of this object (after the wait() method). The producer thread then acquires the thread and continue its operations. The difference between notify() and notifyAll() is notify() arbitrarily picks a thread from this object's waiting pool and places it on the Seeking-lock state; while notifyAll() awakens all the threads in this object's waiting pool. The awaken threads then compete for execution in the normal manner. It is interesting to point out that multithreading is built into the Java language right at the root class java.lang.Object. The synchronization lock is kept in the Object. Methods wait(), notify(), notifyAll() used for coordinating threads are right in the class Object. wait() with timeoutThere are variations of wait() which takes in a timeout value: public final void wait() throws InterruptedException public final void wait(long timeout) throws InterruptedException public final void wait(long timeout, int nanos) throws InterruptedExceptionThe thread will ALSO go to BLOCKED state after the timeout expired. Starvation & DeadlockStarvation is the state where one (or more) thread is deprived of a chance to access an object. The problem can be resolved by setting the correct priorities to all the threads. Deadlock refers to the situation where a thread is waiting for a condition, but somewhere else in the program prevented the condition from being fulfilled, thus, prevented the thread from executing. A classical example, known as "deadly embrace" is as follow: thread 1 is holding the lock to object A and thread 2 is holding the lock to object B. Thread 1 is waiting to acquire the lock to object B and thread 2 is waiting to acquire the lock to object A. Both threads are in deadlock and cannot proceed. If both threads seek the lock in the same order, the situation will not arise. But it is complex to program this arrangement. Alternatively, you could synchronize on another object, instead of object A and B; or synchronize only a portion of a method instead of the entire method. Deadlock can be complicated which may involves many threads and objects and can be hard to detect. Multithreading issues in Swing ApplicationsReferences:
A Swing application runs on multiple threads. Specifically, It has three types of threads:
All the event-handling, painting and screen refreshing codes runs in a single thread, called the event-dispatching thread. This is to ensure that one event handler finishes execution before the next handler starts, and that painting is not interrupted by events. If the event-dispatching thread is starved by another compute-intensive task, the user interface "freezes", and the program becomes unresponsive to user interaction. "Ideally, any task that requires more than 30 to 100 milliseconds should not run on the EDT. Otherwise, users will sense a pause between their input and the UI response." Furthermore, all codes accessing the GUI components should be run on the event-dispatching thread as many of these components are not guaranteed to be thread-safe. Accessing them from the same thread avoids the multithreading issues. In summary,
javax.Swing.SwingUtilities.invokeLater() and invokeAndWait()The invokeLater(Runnable) and invokeAndWait(Runnable) methods schedule the Runnable task in the event-dispatching thread. To avoid threading issues between the main thread (which runs the main() method) and the event-dispatching thread, it is recommended that you use javax.swing.SwingUtilities.invokeLater(Runnable) to create the GUI components on the event-dispatching thread, instead of using the main thread. Recall that Swing components are not guaranteed to be thread-safe and their access shall be confined to a single thread, the EDT. For example, public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JFrame frame = new JFrame("My Swing Application"); frame.setContentPane(new MyMainPanel()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); }The static method SwingUtilities.invokelater() takes a Runnable object (implemented as an anonymous inner class) as its argument, and schedules the task (specified in the run() method) on the event-dispatching thread. You can call invokeLater() from any thread to request the event-dispatching thread to run certain codes, as specified in the run() method of the Runnable argument. The invokeLater() returns immediately, without waiting for the event-dispatching thread to execute the code. If needed, you can use invokeAndWait(), which waits until the event-dispatching thread has executed the specified codes, and returns. For applet, it is recommended to run the GUI construction codes (in init()) via invokeAndWait(). This is to avoid problems caused by init() exits before the completion of GUI construction. For example, public class SwingTemplateJApplet extends JApplet { @Override public void init() { try { SwingUtilities.invokeAndWait(new Runnable() { @Override public void run() { setContentPane(new SwingTemplateJPanel()); } }); } catch (Exception ex) { ex.printStackTrace(); } } }You can also java.awt.EventQueue.invokeLater() in place of the javax.swing.SwingUtilities.invokeLater(). The javax.swing.SwingUtilities.invokeLater() is just a cover for java.awt.EventQueue.invokeLater(). The traces in the earlier examples show that if SwingUtilities.invokeLater() is not used, the event-dispatching thread is started after the setVisible() method. On the other hand, the invokerLater() starts the event-dispatching thread. javax.swing.Timer (JDK 1.2)If you need to update a component after a certain time delay or at a regular time interval, use a timer class, such as javax.swing.Timer (JDK 1.2) or java.util.Timer (JDK 1.3). For javax.swing.Timer, read "animation using javax.swing.Timer". [TODO] java.util.Timer (JDK 1.3) javax.swing.SwingWorker |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
@SuppressWarnings("serial")
public class SwingWorkerCounter extends JPanel {
private JTextField tfCount;
private int count = 0;
JButton btnStartWorker;
private JLabel lblWorker;
public SwingWorkerCounter () {
setLayout(new FlowLayout());
add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
JButton btnCount = new JButton("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
++count;
tfCount.setText(count + "");
}
});
final SwingWorker |
A instance of SwingWorker is designed to run only once. You cannot restart the instance. You need to create a new instance for run the task again.
More Example: "TumbleItem" demo of the Swing Tutorial.
Publishing and Processing Intermediate Results
Other than processing the final result in done(), you can publish() and process() intermediate results as the need arises.
@SafeVarargs protected final void publish(V... chunks) protected void process(ListIn doInBackground(), use publish(V...) to publish one or more
intermediate result(s) of type V. Override the process(List
Example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | import java.awt.*;
import java.awt.event.*;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
@SuppressWarnings("serial")
public class SwingWorkerCounterIntermediateResult extends JPanel {
private JTextField tfCount;
private int count = 0;
JButton btnStartWorker;
private JLabel lblWorker;
public SwingWorkerCounterIntermediateResult () {
setLayout(new FlowLayout());
add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
JButton btnCount = new JButton("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
++count;
tfCount.setText(count + "");
}
});
final SwingWorker |
More Example: "IconDemoApp" demo of Swing Tutorial.
Property Change Event
The doInBackground() fires PropertyChangeEvent to all its PropertyChangeListeners about bound properties changes. There are two bound properties: "state" and "progress". "state" is defined in the nested enum SwingWorker.StateValue, with value of PENDING (SwingWorker instance created), START (doInBackground started) and DONE (doInBackground completed). "progress" is an int, in the range of 0 to 100. You can change the progress value via setProgress() method inside the doInBackground() to fire a PropertyChangeEvent to all its PropertyChangeListeners.
Example
In this example, inside the doInBackground(), we invoke setProgess() to change the progress bound-property value (between 0 to 100), which in turn fires a PropertyChangeEvent. A PropertyChangeListener is defined and registered with this SwingWorker, which shows the progress value on a progress bar. The event-handler runs in the EDT.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
@SuppressWarnings("serial")
public class SwingWorkerCounterProgress extends JPanel {
private JTextField tfCount;
private int count = 0;
JButton btnStartWorker;
private JLabel lblWorker;
JProgressBar pbWorker;
public SwingWorkerCounterProgress () {
setLayout(new FlowLayout());
add(new JLabel("Counter"));
tfCount = new JTextField("0", 10);
tfCount.setEditable(false);
add(tfCount);
JButton btnCount = new JButton("Count");
add(btnCount);
btnCount.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
++count;
tfCount.setText(count + "");
}
});
final SwingWorker |
Summary
Threads are essential to build a responsive graphical user interface. These are the typical situations where a new thread should be used:
- To fork out a new thread for a time-consuming initialization task (such as disk I/O) in the main thread, so that the GUI comes up faster.
- To fork out a new thread for a time-consuming task (within an event handler) in the event dispatch thread, so that the GUI remains responsive.
- Use timer for a repetitive task, which runs at regular time interval or after a certain time delay.
- To fork out a new thread if the operations need to wait for a message from another thread or program.
In addition, the compute-intensive threads must be co-operative and yield control to others.
Thread Pool, Executor, Callable/Future (JDK 1.5)
The thread pool supporting classes are introduced in package java.lang.concurrent, in JDK 1.5.
Thread Pool
A thread pool is a managed collection of threads that are available to execute tasks. When a large number of tasks is executed using a thread pool, the performance improves as the threads are re-cycled to execute the tasks, which reduces the per-task invocation overhead.
To use a thread pool, you can use an implementation of the interface ExecutorService, such as ThreadPoolExecutor or ScheduledThreadPoolExecutor. However, more convenient factory methods are provided in the Executors class as follows:
- Executors.newSingleThreadExecutor(): creates a single background thread.
- Executors.newFixedThreadPool(int numThreads): creates a fixed size thread pool.
- Executors.newCachedThreadPool(): create a unbounded thread pool, with automatic thread reclamation.
The steps of using thread pool are:
- Write you worker thread class which implements Runnable interface. The run() method specifies the behavior of the running thread.
- Create a thread pool (ExecutorService) using one the factory methods provided by the Executors class. The thread pool could have a single thread, a fixed number of threads, or an unbounded number of threads.
- Create instances of your worker thread class. Use execute(Runnable r) method of the thread pool to add a Runnable task into the thread pool. The task will be scheduled and executes if there is an available thread in the pool.
Interface java.util.concurrent.Executor
An Executor object can execute Runnable tasks submitted. The interface declares an abstract method:
public void execute(Runnable r)It executes the given task at some time in the future. The task may be executed in a new thread, in a thread pool, or in the calling thread, depending on the implementation of Executor (e.g. single thread or thread pool)
Interface java.util.concurrent.ExecutorService
Interface ExecutorService declares many abstract methods. The important ones are:
public void shutdown(); publicClass java.util.concurrent.Executors
The class Executors provides factory methods for creating Executor object. For example:
static ExecutorService newSingleThreadExecutor() static ExecutorService newFixedThreadPool(int nThreads) static ExecutorService newCachedThreadPool() static ScheduledExecutorService newSingleThreadScheduledExecutor() static ScheduledExecutorService newScheduledThreadPool(int size)Example
public class WorkerThread implements Runnable { private int workerNumber; WorkerThread(int workerNumber) { this.workerNumber = workerNumber; } public void run() { for (int i = 1; i <= 5; ++i) { System.out.printf("Worker %d: %d\n", workerNumber, i); try { Thread.sleep((int)(Math.random() * 500)); } catch (InterruptedException e) {} } } } import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolTest { public static void main(String[] args) { int numWorkers = Integer.parseInt(args[0]); int threadPoolSize = Integer.parseInt(args[1]); ExecutorService pool = Executors.newFixedThreadPool(threadPoolSize); WorkerThread[] workers = new WorkerThread[numWorkers]; for (int i = 0; i < numWorkers; ++i) { workers[i] = new WorkerThread(i+1); pool.execute(workers[i]); } pool.shutdown(); } } > java ThreadPoolTest 5 2 Worker 1: 1 Worker 2: 1 Worker 1: 2 Worker 1: 3 Worker 2: 2 Worker 1: 4 Worker 2: 3 Worker 1: 5 Worker 3: 1 Worker 3: 2 Worker 2: 4 Worker 3: 3 Worker 2: 5 Worker 3: 4 Worker 3: 5 Worker 4: 1 Worker 4: 2 Worker 5: 1 Worker 4: 3 Worker 5: 2 Worker 5: 3 Worker 4: 4 Worker 5: 4 Worker 5: 5 Worker 4: 5Worker 1 and 2 were first scheduled for execution using the 2 threads in the pool, followed by worker 3, 4 and 5. After the task using the thread completes, the thread is returned to the pool. Another task can then be scheduled and begin execution.
You can use pool.shutdown() to shutdown all the threads in the pool.
Interface java.util.concurrent.Callable and Future
A Callable is similar to a Runnable. However, Callable provides a way to return a result or Exception to the thread that spin this Callable. Callable declares an abstract method call() (instead of run() in the Runnable).
public V call()In the thread pool, instead of using execute(Runnable r), you use submit(Callable r), which returns a Future
The interface Future
Example
import java.util.concurrent.Callable; public class CallableWorkerThread implements CallableOutput with 3 workers, unbound number of threads is as follows:
> java CallableThreadPoolTest 3 Worker 1: 1 Worker 3: 1 Worker 2: 1 Worker 3: 2 Worker 1: 2 Worker 2: 2 Worker 2: 3 Worker 2: 4 Worker 2: 5 Worker 1: 3 Worker 3: 3 Worker 3: 4 Worker 3: 5 Worker 1: 4 Worker 1: 5 worker 1 ended worker 2 ended worker 3 endedOther New Thread Features in JDK 1.5
- Lock
- ReadWriteLock
- Semaphores
- Atomics
- Blocking Queue
[TODO]
LINK TO JAVA REFERENCES & RESOURCES
MORE REFERENCES & RESOURCES
- Swing Tutorial's "Concurrency in Swing".
- John O'Conner, "Improve Application Performance With SwingWorker in Java SE 6" @ http://java.sun.com/developer/technicalArticles/javase/swingworker/.