20. Java Concurrency and multithreading-slipped Conditions

Source: Internet
Author: User

The following is transferred from http://ifeve.com/slipped-conditions/:

The so-called slipped conditions, which means that the condition has been changed by another thread since a thread checks a particular condition into the thread's operation, causing the first thread to perform the wrong operation on that condition. Here's a simple example:

 Public classLock {Private BooleanisLocked =true;  Public voidLock () {synchronized( This){         while(isLocked) {Try{             This. Wait (); } Catch(interruptedexception e) {//Do nothing , keep waiting          }        }      }      synchronized( This) {isLocked=true; }    }     Public synchronized voidunlock () {isLocked=false;  This. Notify (); }}

As we can see, the Lock () method contains two synchronization blocks. The first synchronization block performs a wait operation until islocked becomes false, and the second synchronization block resets islocked to true to lock the lock instance to avoid other threads passing through the lock () method.

We can imagine that if at some point islocked is false, at this time, there are two threads accessing the lock method at the same time. If the first line enters upgradeable into the first synchronization block, this time it will find that islocked is false, if the second thread is allowed to execute at this time, it also enters the first synchronization block, also found that islocked is false. Now two threads have checked for this condition to false, and then they will continue into the second synchronization block and set IsLocked to True.

This scenario is an example of slipped conditions, where two threads check the same condition and then exit the synchronization block, allowing other threads to check the condition before the two threads change the condition. In other words, the condition was checked by a thread that the condition was changed by this thread, and the condition was changed by another thread.

To avoid slipped conditions, the condition checks and settings must be atomic, that is, no other thread will check for this condition while the first thread checks and sets the condition.

The solution to the above problem is simple, simply put islocked = True this line of code moves to the first synchronization block, placed behind a while loop:

 Public classLock {Private BooleanisLocked =true;  Public voidLock () {synchronized( This){         while(isLocked) {Try{             This. Wait (); } Catch(interruptedexception e) {//Do nothing , keep waiting}} isLocked=true; }    }     Public synchronized voidunlock () {isLocked=false;  This. Notify (); }}

Now check and set the islocked condition to be atomically executed in the same synchronization block.

A more realistic example.

Perhaps you will say, I can not write such a lame code, still think slipped conditions is a quite theoretical problem. But the first simple example is just for a better demonstration of slipped conditions.

A fair lock in hunger and fairness may be a more realistic example. Then look at the embedded casing lock the naïve implementation of the deadlock, if we try to solve the embedded casing lock dead problem, it is easy to produce slipped conditions problem. First let's look at the example of a nested casing lock dead:

//Fair Lock Implementation with nested monitor lockout problem Public classFairlock {Private BooleanisLocked =false; PrivateThread Lockingthread =NULL; PrivateList waitingthreads =NewArrayList ();  Public voidLock ()throwsinterruptedexception{queueobject Queueobject=NewQueueobject (); synchronized( This) {waitingthreads.add (queueobject);  while(isLocked | | waitingthreads.get (0)! =queueobject) {        synchronized(queueobject) {Try{queueobject.wait (); }Catch(interruptedexception e) {waitingthreads.remove (queueobject); Throwe;      }}} waitingthreads.remove (Queueobject); IsLocked=true; Lockingthread=Thread.CurrentThread (); }  }   Public synchronized voidunlock () {if( This. lockingthread! =Thread.CurrentThread ()) {      Throw NewIllegalmonitorstateexception ("Calling thread has no locked this lock"); } isLocked=false; Lockingthread=NULL; if(Waitingthreads.size () > 0) {Queueobject queueobject= Waitingthread.get (0); synchronized(queueobject) {queueobject.notify (); }    }  }}

We can see that synchronized (Queueobject) and the queueobject.wait () call in it are embedded in the synchronized (this) block, which leads to the lock-in of the nested casing. To avoid this problem, we must move the synchronized (queueobject) block out of the synchronized (this) block. The code after the move might be something like this:

//Fair Lock Implementation with slipped conditions problem Public classFairlock {Private BooleanisLocked =false; PrivateThread Lockingthread =NULL; PrivateList waitingthreads =NewArrayList ();  Public voidLock ()throwsinterruptedexception{queueobject Queueobject=NewQueueobject (); synchronized( This) {waitingthreads.add (queueobject); }    BooleanMustwait =true;  while(mustwait) {synchronized( This) {mustwait= IsLocked | | Waitingthreads.get (0)! =Queueobject; }      synchronized(queueobject) {if(mustwait) {Try{queueobject.wait (); }Catch(interruptedexception e) {waitingthreads.remove (queueobject); Throwe; }        }      }    }    synchronized( This) {waitingthreads.remove (queueobject); IsLocked=true; Lockingthread=Thread.CurrentThread (); }  }}

Note: Because I only changed the lock () method, only the lock method is shown here.

The lock () method now contains 3 synchronization blocks.

First, synchronized (this) block via mustwait = isLocked | | Waitingthreads.get (0)! = Queueobject checks the value of the internal variable.

The second, synchronized (queueobject) block checks whether the thread needs to wait. It is also possible that other threads have been unlocked at this time, but we will not consider this issue for the moment. We assume that the lock is unlocked, so the line routines immediately exits the synchronized (queueobject) block.

The third, synchronized (this) block will only be executed when mustwait is false. It resets the islocked back to true and then leaves the lock () method.

Imagine what happens if there are two threads calling the lock () method at the same time when the lock is in the unlocked state. First, thread 1 checks to islocked to False, and thread 2 also checks to IsLocked to false. Then, they will not wait, will go to set islocked to true. This is one of the best examples of slipped conditions.

Solve slipped conditions problem

To solve the slipped conditions problem in the previous example, the code in the last synchronized (this) block must move up to the first synchronization block. In order to adapt to this change, the code needs to make some minor changes. Here's the changed code:

//Fair Lock implementation without nested monitor lockout problem,//But with missed signals problem. Public classFairlock {Private BooleanisLocked =false; PrivateThread Lockingthread =NULL; PrivateList waitingthreads =NewArrayList ();  Public voidLock ()throwsinterruptedexception{queueobject Queueobject=NewQueueobject (); synchronized( This) {waitingthreads.add (queueobject); }    BooleanMustwait =true;  while(mustwait) {synchronized( This) {mustwait= IsLocked | | Waitingthreads.get (0)! =Queueobject; if(!mustwait)          {Waitingthreads.remove (queueobject); IsLocked=true; Lockingthread=Thread.CurrentThread (); return; }      }           synchronized(queueobject) {if(mustwait) {Try{queueobject.wait (); }Catch(interruptedexception e) {waitingthreads.remove (queueobject); Throwe; }        }      }    }  }}

We can see that the check and assignment of the local variable mustwait is done in the same synchronous block. You can also see that, even if mustwait is checked out of the synchronized (this) block, in the while (mustwait) clause, the mustwait variable is never assigned outside the synchronized (this) synchronization block. When a thread checks that mustwait is false, it will automatically set the internal conditions (isLocked), so when other threads check the condition, they will find that the value of this condition is now true.

The statement in the synchronized (this) block return; is not required. It's just a small optimization. If a thread certainly does not wait (that is, mustwait is false), then it is not necessary to get it into the synchronized (Queueobject) synchronization block and execute the IF (mustwait) clause.

Attentive readers may notice that the above fair lock implementation is still possible to lose the signal. Imagine a thread calling the lock () method when the Fairlock instance is in a locked state. After the first synchronized (this) block is executed, the value of the mustwait variable is true. Again imagine a cut with lock () thread is through preemption, the thread that owns the lock the thread at this time called the Unlock () method, but look at the implementation of the previous unlock () you will find that it calls the Queueobject.notify (). However, because the thread in lock () has not had time to call Queueobject.wait (), the queueobject.notify () call has no effect, and the signal is lost. If the thread calling lock () calls Queueobject.wait () after another thread calls Queueobject.notify (), the thread will block until the other thread calls the Unlock method, but this will never happen.

The problem of signal loss realized by Fair lock in the article on hunger and fairness we have discussed, transforming Queueobject into a semaphore and providing two methods: Dowait () and donotify (). These methods store and respond to signals inside the queueobject. In this way, the signal is not lost even if donotify () is called before Dowait ().

20. Java Concurrency and multithreading-slipped Conditions

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.