Please support my sponors and make this site possible!!!
Please support our sponsors!

 

Home > Core Java FAQ > Threads FAQ
Threads 
Creating and Controlling (07)   *  Thread Interactions(10)  *  User Threads versus System Threads(04) 
 
Q . Why is thread synchronization important for multithreaded programs?

Ans : 

Thread synchronization provides the tool for ensuring that different threads take proper turns when using shared resources.

     In general, multiple threads in a program run independently of one another. Each follows its sequence of commands, blithely ignorant of other threads or their activities. As long as a thread works with its own, separate data, the most it might suffer from other threads is to be slowed down because of sharing CPU time, if the number of threads is greater than the number of available processors.
     A key property of threads, however, is their ability to directly access shared data. This ability is both a blessing and a curse because it allows threads to:

  • communicate efficiently with each other by manipulating shared data
  • interfere capriciously with each other by manipulating shared data
     When threads act on shared data, timing is the crucial issue. You must always consider how one thread might disrupt the data used by another thread, by executing some of its instructions in between the instructions that the other is executing. (This potential for interleaved execution is true on both single-processor and multiprocessor platforms.) Even code that appears bullet-proof can be slain by an intervening thread. Consider, for example, the getTitleLength method in the following class definition:
     public class SomeClass {
         String title = "Default Title";
         int getTitleLength() {
             if (title != null) {
                 return title.length();
              }
             return -1;    // signal error condition
         }

         void setTitle(String s) {
             title = s;
         }

         void deleteTitle() {
             title = null;
         }
     }
Suppose one thread (thread X) is about to run getTitleLength, and another thread (thread Y) is about to run deleteTitle. Thread X, entering getTitleLength, checks that the title string is non-null before invoking length() on it. Thread Y, however, can interpose between these two steps and invoke deleteTitle. The title string is now null, and thread X's next step will invoke the length method on a null reference—just what the if statement was supposed to prevent!
     Safe multithreaded use of data requires that different threads must operate either on separate data or at different times. Since threads must often share data, the Java programming language provides the synchronized keyword to mark methods or blocks of code that cannot overlap each other in time
Q . What is a monitor?

Ans : 

A monitor is a one-thread-at-a-time set of code-blocks and methods that also provides a wait-notify service.

     When multiple threads access common, changeable data, you must regulate the timing of those threads to ensure that one thread doesn't interfere with another thread's assumptions of data integrity. The Java platform lets you restrict thread interactions by means of monitors. A monitor groups a set of code blocks into a single protected space, such that only one thread at a time can be in the monitor, that is, executing any of the code under the monitor's protection.
     A monitor uses a mutual exclusion lock to protect its region of code. A thread must acquire/lock the lock in order to enter the monitor, and it relinquishes/unlocks the lock when it exits the monitor. While a monitor is locked, it blocks any other threads that attempt to enter it. Monitor locks also allow recursive access: once a thread has the lock, it can run other code that requires the same lock, effectively re-entering the monitor one level down. The lock is not released until the thread holding it completely exits the monitor at all levels. Only at that point can another thread enter the monitor.
     A monitor also provides a wait-notify service to threads. A thread in the monitor can invoke wait, which releases the monitor lock and puts the thread to sleep until it is awakened by a notifyAll (or notify) method invocation.

     You can use many different monitors in a single program to guard different parts of your data. Each monitor is associated with a specific class or object; this connection is often described in terms such as "each object has a monitor," or "each object has a lock." Keep in mind, though, that a monitor provides no inherent protection to its affiliated object. It's still up to you to choose carefully which code to include in monitors in order to protect your data.
     Here's an alternate view of the preceding concept-heavy explanation. Let's call code that belongs to one or another monitor "protected code," and all other code "unprotected code." Imagine that each object in your program owns a unique, reusable ticket. Threads can execute unprotected code freely at any time—no tickets are required. Protected code, however, always requires a thread to have a ticket, and each synchronized statement or method specifies which object that ticket must come from. The thread holds on to the ticket while executing the protected code block or method, and hands the ticket back to its owner object immediately upon exiting the protected code. Because there is only one ticket per object, any other threads needing that ticket are forced to wait and then proceed in an orderly, one-by-one fashion through any protected portion of code.

Q . How does the synchronized keyword work? 

Ans : 

The synchronized keyword marks portions of code as belonging to a monitor: a protected portion of code that enforces a one-thread-at-a-time policy.

     The Java Virtual Machine provides monitors  as the fundamental concept for managing thread synchronization. Part of a monitor's function is to protect a set of code blocks and methods, such that only one thread at a time can execute any of the code belonging to the monitor. Each object is associated with a monitor, and you access a monitor by referring to its affiliated object.
     You cannot directly inspect or manipulate monitors; instead, you define the scope of a monitor implicitly by specifying the code blocks and methods it contains. This is the job of synchronized statements and synchronized methods.
     A synchronized statement specifies an object and a block of code: it flags the code block as belonging to the specified object's monitor. For example, much of the JDK 1.1 code for managing AWT (Abstract Window Toolkit) containment relationships  uses the Component.LOCK object in synchronized statements:

     /* in Component.java (JDK 1.1): */
     static final Object LOCK = new Object();
      // ...
      public void validate() {
          if (!valid) {
             synchronized (Component.LOCK) {
                  valid = true;
             }
         }
     }
     A synchronized method is like a synchronized statement, except that it specifies only the block of code. The entire method body belongs to the target object's monitor (that is, the monitor of the object the method is invoked on). Following is the example from, modified to contain one synchronized statement and two synchronized methods:
     public class SomeClass {
         String title = "Default Title";
         int getTitleLength() {
             /* a synchronized statement */
             synchronized(this) {
                 if (title != null) {
                     return title.length();
                 }
             }
             return -1;    // signal error condition
         }

         /* a synchronized method */
         synchronized void setTitle(String s) {
             title = s;
         }

         /* another synchronized method */
         synchronized void deleteTitle() {
              title = null;
         }
     }
     This code specifies that each SomeClass instance has a monitor containing (at the least) the if statement inside of getTitleLength, the entire body of setTitle, and the entire body of deleteTitle.
     Finally, the following are some key points to remember about the relation between monitors and the synchronized keyword. They were discussed earlier but deserve one more round of emphasis:
  • Monitors define synchronization-protected sets of code blocks.
  • Monitors are the fundamental unit of synchronization. Synchronized statements and synchronized methods merely serve to assign blocks of code to various monitors.
  • Synchronized code is synchronized only with respect to other synchronized code in the same monitor. Unsynchronized code pays no attention to monitors, locks, etc.
  • The monitor/lock associated with an object does not inherently lock the data in the object. Instead, it is like a per-monitor access ticket. Monitors fundamentally protect code, not data.
Q . What objects do static synchronized methods use for locking?

Ans : 

Methods declared as static synchronized are class methods; they must obtain a lock on the class object.

     All synchronized code is associated with one or another object for purposes of locking. Although synchronized methods don't explicitly indicate the object to use for locking, the rule is straightforward: a synchronized instance method obtains the lock for the object it's invoked on, and a synchronized class method obtains the lock for the class object (instance of class Class) that represents the class.
     Synchronized class methods (that is, static synchronized methods) give you a class object implicitly, but for static synchronized statements you must provide the class object explicitly. Two methods provide class objects for you:

  • Object's getClass method; this requires that you have an instance of the class on hand or are willing to create one
  • Class's forName method; this allows you to create a class object from a string representing the class's fully qualified name
     Starting with the JDK 1.1, you can also access class objects directly from a class's class field. For example, Thread.class is a reference to the class object for the Thread class.
     Suppose that you have an instance myTimer of your Timer class; you can then use the first technique:
     Timer myTimer = new Timer();
     void myMethod() {
         // ...
         synchronized (myTimer.getClass()) {
             // ... code that requires synchronization
         }
     }
Suppose next that you want to lock on the AWT's Component class. Component is an abstract class; therefore you cannot create instances of it. In the JDK 1.0.2, you have no choice but to use Class's forName method (and deal with the ClassNotFoundException that forName can throw):
     void anotherMethod() {
         // ...
         try {
             synchronized (Class.forName("java.awt.Component")) {
                 // ... code that requires synchronization
             }
         } catch (ClassNotFoundException e) {
             // ... handle the exception
          }
     }
In the JDK 1.1, the preceding Component example can be simplified considerably:
     /* using JDK 1.1: */
      void anotherMethod() {
         // ...
         synchronized (Component.class) {
             // ... body of synchronized block
         }
     }
Q . How do the wait and notifyAll/notify methods enable cooperation between threads?

Ans :  

They use a monitor as the middleman: wait places the invoking thread on the monitor's waiting list for notifications, and notifyAll (or notify) instructs the monitor to reactivate all (or one) of the threads in its notification list.

     In contrast to the synchronized keyword, which helps threads stay out of each other's way, the wait and notifyAll/notify methods in class Object permit threads to cooperate actively. A thread invoking wait voluntarily suspends its activity, trusting that another thread will invoke notifyAll or notify to eventually reactivate the waiting thread. (Two versions of wait let you specify also a maximum waiting time, such that wait will return even if no notifications are received.) The working of these methods is personified in Table 9.1, from the point of view of a thread invoking the method on an object.
     The wait and notifyAll/notify methods constitute a minimal framework of controlled interaction between threads. The service provided by these methods is:

  • mediated: monitors must serve as middlemen between waiting threads and notifying threads; the threads never communicate directly.
  • anonymous: a waiting thread can't specify which thread will notify/reactivate it, and a notifying thread can't specify which thread(s) it will reactivate.
  • contentless: a reactivated thread receives no indication of what happened to trigger the notification. If the thread needs to know why it was reactivated, it must infer the cause from whatever data it has access to.
  • not guaranteed to be fair: there is no required order in which to notify waiting threads. It is possible for a thread to remain on the wait queue arbitrarily long while other threads are chosen for notification. (Nevertheless, fairness is a desirable trait that virtual machine implementations should aim for.)

Table 9.1: wait, notifyAll, and notify
Method Actions and constraints
wait I deactivate myself, and ask this object's monitor to enter me on its notification waiting list.
I must own the lock on this monitor at the point I invoke wait, but I release the lock as soon as I start waiting.
When I wake up, I must reacquire the lock; this may take some time if one or more other threads beat me to it.
When I acquire the lock, wait will return; I can then continue running.
notifyAll I tell this object's monitor to reactivate all threads on its notification waiting list.
I must own the lock on this monitor at the point I invoke notifyAll.
None of the awakened threads can run until I release the lock.
notify I tell this object's monitor to reactivate only one of its waiting threads (and I can't say which one).
I must own the lock on this monitor at the point I invoke notify.
The awakened thread can't run until I release the lock.

     Finally, keep in mind that invoking notifyAll does not wake up all the threads in an application; it affects only those threads that are waiting on one specific monitor.

Q . How do I achieve the effect of condition variables if the Java platform provides me with only wait and notifyAll/notify methods?

Ans : 

Invoke wait within a while loop that tests for the condition you're interested in, and make sure that other threads invoke notifyAll (or notify) at points where you think or know the condition will change.

     The idea behind a condition variable is that a waiting thread should be reactivated when a specified condition arises, but not otherwise. The Java language does not provide such a service—the interaction provided by the wait, notifyAll, and notify methods is anonymous and contentless, and no conditions are checked prior to reactivating a thread. When a waiting thread is awakened, it receives no information about who triggered the wakeup or why. The newly reactivated thread must determine for itself if the condition it's waiting for has arrived.
     To achieve the effect of a condition variable, place your invocation of wait inside a while statement. (Do not use an if statement—the condition could change while the thread is waiting and it must be rechecked.) For example, the following Thread subclass waits until the amount of free memory drops below a predetermined level and then takes action:

     class MemoryWatcherThread extends Thread {
         long lowMemoryLine = ...;

         public void run() {
             Runtime runtime = Runtime.getRuntime();
              while (true) {
                 synchronized (Runtime.class) {
                     while (runtime.freeMemory() >= lowMemoryLine) {
                         try {
                             wait(5000);  // wait up to 5 seconds
                         } catch (InterruptedException e) {
                         }
                     }
                 }
                 // ... respond to low memory level
             }
         }
     }
(If you're using the JDK 1.0.2, you need to replace the expression Runtime.class above with Runtime.getRuntime.getClass().) In this code, there are two ways a MemoryWatcherThread instance (call it a watcher thread) can become active. First, the invocation of wait specifies a maximum waiting time of five seconds (5000 milliseconds), after which wait will return. Second, any other thread invoking notifyAll (or notify) may wake up the watcher thread before that five-second time limit expires. For whichever reason the watcher thread reactivates, the thread (after acquiring the monitor lock) then goes back to the top of the while loop and checks the free memory level. If the level is not too low, the watcher thread goes into another wait; otherwise it runs its code to deal with the low memory level.
     Continuing the example, objects from other classes can effectively request action from a watcher thread by notifying the monitor in which the watcher thread is waiting:
     /* using JDK 1.1: */
         class Example {
         void checkMemory() {
             synchronized (Runtime.class) {
                 Runtime.class.notifyAll();
             }
         }
         // ...
     }
Using notifyAll ensures that all threads waiting on the monitor will be notified—it lets each waiting thread decide for itself whether to reactivate or continue waiting. Using notify, on the other hand, would wake up only one waiting thread, and quite possibly the wrong one. In general, you shouldn't use notify unless you know precisely which single thread will be waiting for it, or you know that any of the waiting threads could respond equally well to the notification.
Q . How do I make one thread wait for one or more other threads to finish?

Ans : 

Use one of Thread's join methods.

     Threads are designed for the most part to run independently of one another. Typically, one thread neither knows nor cares when another thread finishes executing. If you need one of your threads to wait for another thread to finish, however, you can use one of the join methods in the Thread class.
     Invoking join involves two threads even though the method invocation only shows one, as in thatThread.join(). The second thread is implicit—it is the currently executing thread. The invocation of join causes the currently executing thread to suspend execution until the target thread (thatThread in the above example) finishes executing. In other words, invoking join on a thread is like waiting to be notified of that thread's death.
     Also, like the wait methods, there is a family of join methods. The basic no-parameter method, join(), waits indefinitely until the target thread dies. You can also specify one or two arguments as a maximum duration to wait:

  • join(long millis): wait no more than the specified number of milliseconds (thousandths of a second).
  • join(long millis, int nanos): wait no more than the specified number of milliseconds and nanoseconds (billionths of a second).
     A key difference from wait is that join need not and should not hold a monitor lock when it is invoked.
     For reference, Table summarizes the methods that can suspend a thread's execution (without irrevocably terminating it):

Table : Methods that can Suspend Thread Execution

Class
Methods
Thread sleep(long millis), sleep(long millis, int nanos)
join(), join(long millis), join(long millis, int nanos)
suspend()
Object wait(), wait(long millis), wait(long millis, int nanos)
Q . What do I use the yield method for?

Ans : 

You invoke yield in one thread to give other threads more chances to run.

     Any platform that runs more than one thread per processor needs to have a thread scheduler—a strategy for deciding when to switch execution from one thread to another, and which other thread to switch to. A thread scheduler typically regulates turn-taking according to thread states and thread priorities. The following thread states distinguish among which threads are executing or have the potential to execute:

  • running: currently executing
  • runnable: ready to run, but not currently executing
  • waiting: not ready to run; must first be reactivated by a timer, a notification, or a resume method invocation
  • dead: finished executing, cannot run anymore
     By definition, only the running and runnable threads are considered when the scheduler picks the next thread to run.
     Thread priorities further differentiate among the threads waiting to run. Whenever a thread scheduler has a choice between a lower and higher-priority thread, it is supposed to prefer the higher-priority one. There are no guarantees, though. Thread priorities should be considered at best as hints, rather than as reliable tools for design.
     The Java Virtual Machine specification leaves many details of thread scheduling open to the implementation. For example, the thread scheduler may or may not time-slice, that is, interrupt a currently executing thread after a given amount of time to give another thread a turn. In the JDK (1.0.2 and 1.1), for example, the Windows NT/95 implementation provides time-slicing, whereas the Solaris implementation does not.
     The yield method gives you explicit control over a small piece of the thread-scheduling picture. By invoking yield, a thread voluntarily ends its turn at executing, so that the thread scheduler can choose which thread to run next. (That thread can even be the same one that just invoked yield, if the scheduler so chooses.) Using yield judiciously, such as in computation-intensive loops, enhances turn-taking and fairness among threads, especially when running on a virtual machine implementation that does not provide time-slicing. It also helps your program behave more consistently across platforms.
     Moral: Time-slicing and thread priorities are dangerously variable across virtual machine implementations; for program correctness, design with synchronization and yield.
Q . Does the Java Virtual Machine protect me against deadlocks?

Ans : 

Yes and no: a Java Virtual Machine implementation should be designed to avoid deadlocks within its own code, but it cannot prevent your code from deadlocking.

     A deadlock occurs when two or more threads each hold a resource that another is waiting for—neither thread can proceed to the point where it would free up the resource that the other one is waiting for. In Java code, the precious resource is usually a monitor lock needed for a synchronized method or synchronized block. If your code holds one lock and then tries to acquire another lock, you run the risk of getting deadlocked.
     The prototypical scenario for a deadlock is the following:

  1. Thread 1 includes code that acquires the lock for object X (call it lock X) and, while holding that lock, needs to acquire the lock for object Y (call it lock Y).

  2. Thread 2 includes code that acquires lock Y and, while holding that lock, needs to acquire lock X.

  3. Thread 1 runs, acquires lock X, and, before acquiring lock Y, gets put on hold by the thread scheduler, giving another thread a turn to run.

  4. Thread 2 runs, acquires lock Y, and then tries to acquire lock X. Thread 2 blocks at this point because it can't get the lock—thread 1 is holding it. The thread scheduler tries to find another runnable thread.

  5. Thread 1 runs up to the point it tries to acquire lock Y. Thread 1 then blocks because it can't get the lock—thread 2 is holding it. Threads 1 and 2 are now deadlocked. Neither can move forward because it needs a resource that the -other is holding, and neither can release the resource it's holding until after it has moved forward.
     The Java Virtual Machine does not attempt to prevent your program from deadlocking as it executes—that's simply too hard a problem to solve in general. A virtual machine implementation, however, can make it easier to diagnose and pinpoint cases of deadlock after they've happened. The JDK (1.0.2 and 1.1), for instance, lets you trigger full dumps of the threads and monitors currently used by the virtual machine. As your program is running, you can send a quit signal (ctrl-break on Win32, ctrl-backslash on Solaris) and get a current picture of the threads and monitors at work in your virtual machine. Following is an example triggered on Solaris while running a DeadlockExample class:
     Full thread dump:
         "Thread-5" (TID:0xe6f009f8, sys_thread_t:0xe6a61de0) prio=5

     DeadlockThread.grabSecondLock(DeadlockExample.java:100)

     DeadlockThread.grabFirstLock(DeadlockExample.java:95)
             DeadlockThread.grabLocks(DeadlockExample.java:88)
             DeadlockThread.run(DeadlockExample.java:68)
         "Thread-4" (TID:0xe6f00998, sys_thread_t:0xe6a91de0) prio=5

     DeadlockThread.grabSecondLock(DeadlockExample.java:100)

     DeadlockThread.grabFirstLock(DeadlockExample.java:95)
             DeadlockThread.grabLocks(DeadlockExample.java:88)
             DeadlockThread.run(DeadlockExample.java:68)
         "Finalizer thread" (TID:..., sys_thread_t:...) prio=1
         "Async Garbage Collector" (TID:..., sys_thread_t:...) prio=1
         "Idle thread" (TID:..., sys_thread_t:...) prio=0 *current thread*
         "clock handler" (TID:0xe6f00180, sys_thread_t:0xe6bf1de0) prio=11
         "main" (TID:0xe6f00150, sys_thread_t:0x39990) prio=7
             DeadlockExample.main(DeadlockExample.java:31)
     Monitor Cache Dump:
              unknown key (key=0xe6af1de0):     unowned
             Waiting to be notified:
                 "Async Garbage Collector"
              java.lang.String@...:  monitor owner e6a61de0: "Thread-5"
             Waiting to enter:
                 "Thread-4"
              java.lang.String@...:  monitor owner e6a91de0: "Thread-4"
             Waiting to enter:
                  "Thread-5"
              unknown key (key=0x39990):     unowned
             Waiting to be notified:
                         "main"
This thread-and-monitor dump reveals that Thread-4 and Thread-5 are deadlocked. The Full thread dump section shows that Thread-4 and Thread-5 are each stuck in their own grabSecondLock method. The Monitor Cache Dump shows why: Thread-5 owns one lock (monitor owner) that Thread-4 is waiting for (Waiting to enter), and Thread-4 owns a lock that Thread-5 is waiting for.
     When you suspect your program is deadlocked, trigger a thread-and-monitor dump and check first for this telltale waiting-to-enter, monitor-owner pattern. It would be nice if future implementations automated this check.
Q . I have several worker threads. I want my main thread to wait for any of them to complete, and take action as soon as any of them completes. I don't know which will complete soonest, so I can't just call Thread.join on that one. How do I do it?

Ans : 

You need to use the wait/notify mechanism to allow any of the worker threads to wake up your main thread when the worker has completed.

 

Copyright © 2000 javafaq.com. All rights reserved