|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 6
The Java Concurrency Utilities framework also exposes the low-level compare-and-swap (CAS) hardware instruction, variants of which are commonly supported by modern processors. CAS is much more lightweight than Java's
monitor-based synchronization mechanism and is used to implement some highly scalable concurrent classes. The CAS-based java.util.concurrent.locks.ReentrantLock class, for instance, is more performant than the equivalent monitor-based synchronized primitive. ReentrantLock offers more control over locking. (In Part 2 I'll explain more about how CAS works in java.util.concurrent.)
The Java Concurrency Utilities framework includes long nanoTime(), which is a member of the java.lang.System class. This method enables access to a nanosecond-granularity time source for making relative time measurements.
In the next sections I'll introduce three useful features of the Java Concurrency Utilities, first explaining why they're so important to modern concurrency and then demonstrating how they work to increase the speed, reliability, efficiency, and scalability of concurrent Java applications.
In threading, a task is a unit of work. One problem with low-level threading in Java is that task submission is tightly coupled with a task-execution policy, as demonstrated by Listing 1.
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
class Server
{
public static void main(String[] args) throws IOException
{
ServerSocket socket = new ServerSocket(9000);
while (true)
{
final Socket s = socket.accept();
Runnable r = new Runnable()
{
@Override
public void run()
{
doWork(s);
}
};
new Thread(r).start();
}
}
static void doWork(Socket s)
{
}
}
The above code describes a simple server application (with doWork(Socket) left empty for brevity). The server thread repeatedly calls socket.accept() to wait for an incoming request, and then starts a thread to service this request when it arrives.
Because this application creates a new thread for each request, it doesn't scale well when faced with a huge number of requests. For example, each created thread requires memory, and too many threads may exhaust the available memory, forcing the application to terminate.
You could solve this problem by changing the task-execution policy. Rather than always creating a new thread, you could use a thread pool, in which a fixed number of threads would service incoming tasks. You would have to rewrite the application to make this change, however.
Using the Java Concurrency Utilities -- more tutorials on JavaWorld:
java.util.concurrent to work around deadlock and similar threading pitfalls.
CountDownLatch and Executors.newFixedThreadPool concurrency utilities.
java.util.concurrent classes were used to optimize thread use for faster performance in a real-world application.
ExecutorService class with ForkJoinPool in a web crawler.
Popular articles in the Java 101 series