Java multithreading-Thread communication instance explanation _java

Source: Internet
Author: User

The goal of thread communication is to enable threads to send signals to each other. On the other hand, thread communication enables a thread to wait for signals from other threads.

    1. Communicating through shared objects
    2. Busy waiting
    3. Wait (), notify () and Notifyall ()
    4. The missing signal
    5. False wake-up
    6. Multithreading waits for the same signal
    7. Do not invoke wait () on a constant string or global object

Communicating through shared objects

A simple way to send a signal between threads is to set the signal value in the variable of the shared object. Thread A sets the Boolean member variable hasdatatoprocess to true in a synchronized block, and thread B reads hasdatatoprocess This member variable in the synchronization block. This simple example uses a signal-holding object and provides set and check methods:

public class mysignal{

 protected Boolean hasdatatoprocess = false;

 Public synchronized Boolean hasdatatoprocess () {return
 this.hasdatatoprocess;
 }

 Public synchronized void Sethasdatatoprocess (Boolean hasData) {
 this.hasdatatoprocess = hasData;
 }

}

Threads A and B must obtain a reference to a Mysignal shared instance for communication. If they hold references that point to different mysingal instances, they will not be able to detect each other's signals. The data that needs to be processed can be stored in a shared buffer, which is stored separately from the Mysignal instance.

Busy Waiting (Busy wait)

The thread B preparing to process the data is waiting for the data to become available. In other words, it is waiting for a signal from thread A, which causes hasdatatoprocess () to return true. Thread B runs in a loop to wait for this signal:

protected Mysignal sharedsignal = ...

...

while (!sharedsignal.hasdatatoprocess ()) {
 //do nothing ... busy waiting
}

Wait (), notify () and Notifyall ()

Busy waiting does not make efficient use of the CPU running the waiting thread unless the average wait time is very short. Otherwise, it is wiser to let the waiting thread go to sleep or not to run until it receives the signal it waits for.

Java has a built-in wait mechanism to allow threads to become inactive when waiting for a signal. The Java.lang.Object class defines three methods, wait (), notify (), and Notifyall () to implement this waiting mechanism.

Once a thread calls the wait () method of any object, it becomes inactive until another thread invokes the Notify () method of the same object. In order to invoke wait () or notify (), the thread must first obtain the lock on that object. In other words, the thread must call Wait () or notify () in the synchronization block. The following is a modified version of Mysingal-using the mywaitnotify of Wait () and notify ():

public class monitorobject{
} public

class mywaitnotify{

 monitorobject mymonitorobject = new Monitorobject ();

 public void dowait () {
 synchronized (mymonitorobject) {
  try{
  mymonitorobject.wait ();
  } catch ( Interruptedexception e) {...}

 }} public void Donotify () {
 synchronized (mymonitorobject) {
  mymonitorobject.notify ();
 }
 }
}

The waiting thread will call Dowait (), and the wake-up thread will invoke Donotify (). When a thread calls an object's Notify () method, a thread in all the threads that is waiting for the object is awakened and allowed to execute (Chong: The thread that will be awakened is random and cannot specify which thread to wake from). A Notifyall () method is also provided to awaken all threads that are waiting for a given object.

As you can see, both the waiting thread and the wake thread Invoke Wait () and notify () in the sync block. This is mandatory! A thread cannot invoke wait (), notify (), or notifyall () if it does not hold an object lock. Otherwise, a Illegalmonitorstateexception exception is thrown.

(Chong: The JVM implements this, and when you call wait it first checks to see if the current thread is the owner of the lock, not the illegalmonitorstateexcept.) )

But how is that possible? When the waiting thread executes inside the sync block, does it not always hold the lock on the monitor object (Mymonitor object)? Does the wait thread not block the wake Thread's sync block entering Donotify ()? The answer is: certainly not. Once the thread invokes the Wait () method, it frees the lock on the monitor object it holds. This will allow other threads to also invoke wait () or notify ().

Once a thread is awakened, it cannot immediately exit the method call to wait () until the Notify () is invoked

public class mywaitnotify2{

 monitorobject mymonitorobject = new Monitorobject ();
 Boolean wassignalled = false;

 public void dowait () {
 synchronized (mymonitorobject) {
  if (!wassignalled) {
  try{
   Mymonitorobject.wait ();
   } catch (Interruptedexception e) {...}
  }}
  Clear signal and continue running.
  wassignalled = false;
 }
 }

 public void Donotify () {
 synchronized (mymonitorobject) {
  wassignalled = true;
  Mymonitorobject.notify ();}}



The thread exits its own sync block. In other words: The awakened thread must regain the lock on the monitor object before it can exit the method call for Wait (), because a wait method call runs inside the synchronization block. If multiple threads are awakened by Notifyall (), then only one thread at a time will be able to exit the wait () method, because each thread must obtain a lock on the monitor object before exiting Wait ().

Lost signal (missed signals)

The Notify () and Notifyall () methods do not save the method that calls them, because when the two methods are invoked, it is possible that no threads are in the waiting state. The signal is discarded after the notification. Therefore, if a thread calls notify () before the notified thread calls Wait (), the waiting thread will miss the signal. This may or may not be a problem. However, in some cases, this may cause the waiting thread to wait forever and not wake up because the thread missed the wake-up signal.

In order to avoid losing the signal, they must be kept in the signal class. In the mywaitnotify example, the notification signal should be stored in a member variable of the Mywaitnotify instance. The following is a modified version of Mywaitnotify:

public class mywaitnotify2{

 monitorobject mymonitorobject = new Monitorobject ();
 Boolean wassignalled = false;

 public void dowait () {
 synchronized (mymonitorobject) {
  if (!wassignalled) {
  try{
   Mymonitorobject.wait ();
   } catch (Interruptedexception e) {...}
  }}
  Clear signal and continue running.
  wassignalled = false;
 }
 }

 public void Donotify () {
 synchronized (mymonitorobject) {
  wassignalled = true;
  Mymonitorobject.notify ();}}


Note that the Donotify () method sets the wassignalled variable to True before calling notify (). Also, note that the Dowait () method checks the wassignalled variable before calling wait (). In fact, if no signal is received in the time period between the previous dowait () call and this dowait () call, it will only call wait ().

(Chong: To avoid the loss of a signal, use a variable to save whether it has been notified.) Before notify, set yourself to have been notified. After waiting, set yourself without being notified and need to wait for a notification. )

False wake-up

For inexplicable reasons, threads may wake up without invoking notify () and Notifyall (). This is called false Awakening (spurious wakeups). Wake up for no reason.

If a false wakeup occurs in the MyWaitNotify2 dowait () method, the waiting thread can perform subsequent operations even if it does not receive the correct signal. This can cause serious problems with your application.

To prevent false awakening, the member variable that holds the signal is checked in a while loop, not in the IF expression. Such a while loop is called a spin lock (Chong: This approach is prudent, the current JVM implementation of the spin will consume the CPU, if the long time does not call the Donotify method, the Dowait method will always spin, the CPU will consume too much). The awakened thread spins until the condition in the spin lock (while loop) changes to false. This is shown in the modified version of the following MyWaitNotify2:

public class mywaitnotify3{

 monitorobject mymonitorobject = new Monitorobject ();
 Boolean wassignalled = false;

 public void dowait () {
 synchronized (mymonitorobject) {while
  (!wassignalled) {
  try{
   Mymonitorobject.wait ();
   } catch (Interruptedexception e) {...}
  }}
  Clear signal and continue running.
  wassignalled = false;
 }
 }

 public void Donotify () {
 synchronized (mymonitorobject) {
  wassignalled = true;
  Mymonitorobject.notify ();}}


Note that the wait () method is in the while loop, not in an if expression. If the waiting thread wakes up without receiving a signal, the wassignalled variable becomes the False,while loop and then executes again, prompting the waking thread to return to the waiting state.

Multiple threads waiting for the same signal

If you have multiple threads waiting, awakened by Notifyall (), but only one is allowed to continue, using the while loop is also a good way to do it. Only one thread can get a monitor object lock at a time, meaning that only one thread can exit the wait () call and clear the WASSIGNALLED flag (set to false). Once this thread exits the dowait () synchronization block, the other thread exits the wait () call and checks the wassignalled variable value in the while loop. However, this flag has been first awakened by the line Cheng except, so the rest of the waking thread will return to the waiting state until the next signal arrives.

Do not call wait () in a string constant or global object

(Chong: The string constant in this chapter refers to a variable that has a value of constant)

A previous version of this article uses a string constant ("") as a pipe object in the mywaitnotify example. Here's the example:

public class mywaitnotify{

 String mymonitorobject = "";
 Boolean wassignalled = false;

 public void dowait () {
 synchronized (mymonitorobject) {while
  (!wassignalled) {
  try{
   Mymonitorobject.wait ();
   } catch (Interruptedexception e) {...}
  }}
  Clear signal and continue running.
  wassignalled = false;
 }
 }

 public void Donotify () {
 synchronized (mymonitorobject) {
  wassignalled = true;
  Mymonitorobject.notify ();}}


The problem with the invocation of Wait () and notify () in the empty string as the synchronized block (or other constant string) of the lock is that the jvm/compiler converts the constant string to the same object internally. This means that even if you have 2 different mywaitnotify instances, they all refer to the same empty string instance. It also implies the risk that a thread calling dowait () on the first mywaitnotify instance is awakened by a thread that invokes Donotify () on the second mywaitnotify instance. This picture can be drawn as follows:

At first it may not seem like a big problem. After all, if Donotify () is invoked on the second mywaitnotify instance, what really happens is that threads A and B are awakened incorrectly. The awakened thread (A or B) examines the signal value in the while loop and returns to the wait state, because Donotify () is not invoked on the first mywaitnotify instance, which is exactly the instance it waits for. This is equivalent to triggering a false wake-up call. Thread A or B wakes when the signal value is not updated. But the code handles the situation, so the thread returns to the wait state. Remember that even if 4 threads invoke Wait () and notify () on the same shared string instance, the signals in dowait () and donotify () are also saved separately by 2 mywaitnotify instances. A donotify () call on the MYWAITNOTIFY1 may wake the MyWaitNotify2 thread, but the signal value will only be stored in MyWaitNotify1.

The problem is that because donotify () only invokes notify () instead of Notifyall (), only one thread can be awakened, even if there are 4 threads waiting on the same instance of the same string (an empty string). So if a thread A or B is given a wake-up call to C or D, it checks its own signal value to see if there is any signal being received and then back to the wait state. and C and D are not awakened to check the signal values they actually receive, so the signal is lost. This situation is equivalent to the previous problem of losing the signal. C and D have been sent signals, but cannot respond to the signal.

If the Donotify () method calls Notifyall () instead of notify (), all waiting threads are awakened and the signal values are checked in turn. Threads A and B will return to the wait state, but C or D only one thread notices the signal and exits the dowait () method call. Another in C or D will return to the wait state, because the signal-acquiring thread clears the signal value (set to False) during the exit dowait ().

After looking at this section, you may try to use Notifyall () instead of notify (), but this is a bad idea for performance. In situations where only one thread can respond to a signal, there is no reason to wake all threads at a time.

So: in the Wait ()/notify () mechanism, do not use global objects, string constants, and so on. The corresponding unique object should be used. For example, each instance of a MYWAITNOTIFY3 has a monitor object of its own, instead of calling Wait ()/notify () on an empty string.

The above is about Java multithreading, thread communication data collation, follow-up continue to supplement the relevant information, thank you for your support of this site!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.