Threads in Java allow the use of multiple processors or multiple cores in one processor more efficiently. On a single processor, threads provide for concurrent operations such as overlapping I/O with processing.
Java supports multithreaded programming features with the Thread
class and the Runnable
interface.
Threads can be created two ways, either by extending java.lang.Thread
or by implementing java.lang.Runnable
.
Extending the Thread
class and overriding the run()
method can create a threadable class. This is an easy way to start a thread:
class
Comet
extends
Thread
{
public
void
run
()
{
System
.
out
.
println
(
"Orbiting"
);
orbit
();
}
}
Comet
halley
=
new
Comet
();
hally
.
run
();
Remember that only one superclass can be extended, so a class that extends Thread
cannot extend any other superclass.
Implementing the Runnable
functional interface and defining its run()
method can also create a threadable class.
class
Asteroid
implements
Runnable
{
public
void
run
()
{
System
.
out
.
println
(
"Orbiting"
);
orbit
();
}
}
Asteroid
majaAsteroid
=
new
Asteroid
();
Thread
majaThread
=
new
Thread
(
majaAsteroid
);
majaThread
.
run
();
A single runnable instance can be passed to multiple thread objects. Each thread performs the same task, as shown here after the use of a Lambda Expression:
Runnable
asteroid
=
()
->
{
System
.
out
.
println
(
"Orbiting"
);
orbit
();
};
Thread
asteroidThread1
=
new
Thread
(
asteroid
);
Thread
asteroidThread2
=
new
Thread
(
asteroid
);
asteroidThread1
.
run
();
asteroidThread2
.
run
();
Enumeration Thread.state
provides six thread states, as depicted in Table 14-1.
Thread state | Description |
| A thread that is created but not started |
| A thread that is available to run |
| An “alive” thread that is blocked waiting for a monitor lock |
| An “alive” thread that calls its own |
| An “alive” thread that is waiting on another thread for a specified period of time; sleeping |
| A thread that has completed |
The valid range of priority values is typically 1 through 10, with a default value of 5. Thread priorities are one of the least portable aspects of Java, as their range and default values can vary among Java Virtual Machines (JVMs). Using MIN_PRIORITY
, NORM_PRIORITY
, and MAX_PRIORITY
can retrieve priorities.
System
.
out
.
(
Thread
.
MAX_PRIORITY
);
Lower priority threads yield to higher priority threads.
Table 14-2 contains common methods used for threads from the Thread
class.
Method | Description |
| Returns the thread’s priority |
| Returns the thread’s state |
| Interrupts the thread |
| Returns the thread’s alive status |
| Checks for interruption of the thread |
| Causes the thread that invokes this method to wait for the thread that this object represents to finish |
| Sets the thread’s priority |
| Places the thread into a runnable state |
Table 14-3 contains common methods used for threads from the Object
class.
Method | Description |
| Tells a thread to wake up and run |
| Tells all threads that are waiting on a thread or resource to wake up, and then the scheduler will select one of the threads to run |
| Pauses a thread in a wait state until another thread calls |
Calls to wait()
and notify()
throw an InterruptedException
if called on a thread that has its interrupted flag set to true.
Table 14-4 contains common static methods used for threads from the Thread
class (i.e., Thread.sleep(1000)
).
Method | Description |
| Returns number of threads in the current thread’s group |
| Returns reference to the currently running thread |
| Checks for interruption of the currently running thread |
| Blocks the currently running thread for |
| Pauses the current thread to allow other threads to run |
The synchronized
keyword provides a means to apply locks to blocks and methods. Locks should be applied to blocks and methods that access critically shared resources. These monitor locks begin and end with opening and closing braces. Following are some examples of synchronized blocks and methods.
Object instance t
with a synchronized lock:
synchronized
(
t
)
{
// Block body
}
Object instance this
with a synchronized lock:
synchronized
(
this
)
{
// Block body
}
Method raise()
with a synchronized lock:
// Equivalent code segment 1
synchronized
void
raise
()
{
// Method Body
}
// Equivalent code segment 2
void
raise
()
{
synchronized
(
this
)
{
// Method body
}
}
Static method calibrate()
with a synchronized lock:
class
Telescope
{
synchronized
static
void
calibrate
()
{
// Method body
}
}
The concurrent utilities provide additional means to apply and manage concurrency.
Java 2 SE 5.0 introduced utility classes for concurrent programming. These utilities reside in the java.util.concurrent
package, and they include executors, concurrent collections, synchronizers, and timing utilities.
The class ThreadPoolExecutor
as well as its subclass ScheduledThreadPoolExecutor
implement the Executor
interface to provide configurable, flexible thread pools. Thread pools allow server components to take advantage of the reusability of threads.
The class Executors
provides factory (object creator) methods and utility methods. Of them, the following are supplied to create thread pools:
newCachedThreadPool()
newFixedThreadPool(int nThreads)
newScheduledThreadPool(int corePoolSize)
newSingleThreadExecutor()
newSingleThreadScheduledExecutor()
The following example demonstrates usage of the newFixedThreadPool
factory method:
import
java.util.concurrent.Executors
;
import
java.util.concurrent.ExecutorService
;
public
class
ThreadPoolExample
{
public
static
void
main
()
{
// Create tasks
// (from 'class RTask implements Runnable')
RTask
t1
=
new
RTask
(
"thread1"
);
RTask
t2
=
new
RTask
(
"thread2"
);
// Create thread manager
ExecutorService
threadExecutor
=
Executors
.
newFixedThreadPool
(
2
);
// Make threads runnable
threadExecutor
.
execute
(
t1
);
threadExecutor
.
execute
(
t2
);
// Shutdown threads
threadExecutor
.
shutdown
();
}
}
Even though collection types can be synchronized, it is best to use concurrent thread-safe classes that perform equivalent functionality, as represented in Table 14-5.
Collection class | Thread-safe equivalent |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Synchronizers are special-purpose synchronization tools. Available synchronizers are listed in Table 14-6.
The TimeUnit
enumeration is commonly used to inform time-based methods how a given timing parameter should be evaluated, as shown in the following example. Available TimeUnit
enum constants are listed in Table 14-7.
// tyrLock (long time, TimeUnit unit)
if
(
lock
.
tryLock
(
15L
,
TimeUnit
.
DAYS
))
{...}
//15 days