Thread Lifecycle
A thread is born by instantiating a subclass of the Thread class, or by
instantiating a Thread object with a Runnable argument. A thread enters
a runnable state when its start() method is invoked. The JVM uses
a priority-based scheduling algorithm to determine which thread to execute.
A thread enters a running state when its run() method is invoked by the
scheduler. However, a thread can transition into a blocked state if it needs
to acquire a lock or if it waits for a notification from another thread. A thread
dies after the run() method completes.
Synchronization
Synchronization is required on mutable resources because the operations
of multiple threads can interleave. For example, the c++ operation may appear
to be atomic, but it is actually composed of three steps: read the value of c,
increment the value of c, and write the value of c. If one thread reads the value
of c and enters a blocked state, a second thread could read the same value and
the counter would only be incremented by one. Synchronizing access to the c++
operation would force all three steps to be completed atomically.
Although synchronization can prevent thread interference and memory
consistency errors, it can also degrade performance and open up the possibility
of a deadlock. A deadlock occurs when two threads wait for each to proceed.
The Synchronize Keyword
The synchronize keyword is used to designate synchronized methods and
statements. A synchronized block of code is guarded by a lock that can only
be acquired by one thread at a time. In a synchronized static method,
the singleton Class object is implicitly used as the lock. In a synchronized non-
static method, the object instance is implicitly used as the lock. In a
synchronized statement, the lock must be provided as an argument. Multiple
locks allow for more granular levels of synchronization.
For example, imagine a Counter class with a synchronized count() method.
If the count() method was static, only one thread in the application could
increment the counter at a time. If the count() method was non-static,
multiple Counter objects could be instantiated and incremented concurrently.
If the Counter class had two separate variables and two separate count()
methods, each method could use synchronized statements with different locks
so that both variables could be incremented concurrently.
|