"Java Concurrency Programming" 20: Concurrency new attribute-lock lock and condition variable (including code) __reentrantlock

Source: Internet
Author: User
Tags cas finally block lock queue mutex

Reprint Please indicate the source: http://blog.csdn.net/ns_code/article/details/17487337


Simple use of lock lock

The new lock mechanism--java.util.concurrent.locks explicit mutex in Java 5: The Lock interface, which provides a more extensive locking operation than synchronized. The lock interface has 3 classes that implement it: Reentrantlock, Reetrantreadwritelock.readlock, and Reetrantreadwritelock.writelock, that is, re-lock, read-lock, and write-lock. Lock must be explicitly created, locked, and released, and in order to be able to use more functionality, it is typically instantiated with Reentrantlock. In order to ensure that the lock will eventually be released (there may be an abnormal occurrence), to place the mutex in a try statement block and release the lock in a finally statement block, especially when there is a return statement, the return statement must be placed in the try phrase to ensure that unlock () does not occur prematurely. Thus exposing the data to the second task. Therefore, the general form of lock lock and release locks is as follows:

     Lock lock = new Reentrantlock ()//The default use of unfair locks, if you want to use a fair lock, you need to pass in the argument true
     ...
     Lock.lock ();
     try {
          //Update object status
         //catch exception, revert to original invariant constraint
        if necessary//if there is return statement, put it here
    } finally {
            lock.unlock ();        The lock must be released in the finally block
    


comparison between Reetranklock and synchronized
Performance Comparison

In JDK1.5, synchronized is inefficient. Because this is a heavyweight operation, its greatest performance impact is the implementation, suspend the thread and recover the thread of the operation need to go into the kernel state of completion, these operations to the system's concurrency has brought great pressure. By contrast, using the lock object provided by Java, the performance is higher. Brian Goetz a set of throughput comparisons for both locks in JDK1.5, single core processors, and dual-Xeon processors, and found that synchronized throughput was significantly degraded in a multithreaded environment, And Reentranklock can basically remain at the same level of stability. But rather than reetrantlock performance, it is better to say that synchronized still have a lot of room for optimization, So to the JDK1.6, has changed, synchronize added a lot of optimization measures, there are adaptive spin, lock elimination, lock coarsening, lightweight locks, bias locks and so on. The performance of the Synchronize on the JDK1.6 is no worse than the lock. Officials also say they are also more supportive of synchronize, and there is room for optimization in future releases, so it is a priority to use synchronized to synchronize synchronized when the requirements are met.


Below is an analysis of the following two kinds of locking mechanism of the underlying implementation strategy.

The most important problem of mutex synchronization is the performance problem caused by thread blocking and awakening, so this kind of synchronization is called blocking synchronization, and it belongs to a pessimistic concurrency policy, that is, the thread gets an exclusive lock. An exclusive lock means that other threads can only rely on blocking to wait for the thread to release the lock. When the CPU conversion thread is blocked, it will cause the thread context to switch, and when there are many threads competing for the lock, the CPU frequent context switching will cause low efficiency. This concurrency strategy is used by synchronized.

With the development of instruction set, we have another choice: optimistic concurrency based on conflict detection strategy, popular speaking is the advanced operation, if no other thread contention to share data, then the operation succeeds, if the shared data is contention, resulting in a conflict, Then there are other compensation measures (the most common compensation measure is to continue to pick up until the trial is successful), many implementations of this optimistic concurrency policy do not need to suspend the thread, so this synchronization is called Non-blocking synchronization. This concurrency strategy is used by Reetrantlock.

In an optimistic concurrency strategy, the need for action and conflict detection is atomic, and it relies on hardware directives to ensure that this is a CAS operation (Compare and Swap). After JDK1.5, the Java program can use CAS operations only. We can further study the source code of Reentrantlock, will find that one of the more important way to get the lock is compareandsetstate, here is actually called the CPU to provide special instructions. Modern CPUs provide instructions to automatically update shared data, and can detect interference from other threads, and Compareandset () replaces locks. This algorithm is called a non-blocking algorithm, meaning that a thread's failure or suspension should not affect the failure or suspension of other threads.

In Java 5, special atomic variables, such as injection Automicinteger, Automiclong, automicreference, are introduced, which provide such as: Compareandset (), Methods such as Incrementandset () and getandincrement () use CAS operations. Therefore, they are all atomic methods guaranteed by hardware directives.


Use comparison basic syntax, reentrantlock and synchronized are very similar, they all have the same thread reentrant characteristics, but the code is a little different, a representation of the API level of the mutex (lock), A mutex (synchronized) that behaves as a primary grammatical level. Reentrantlock has added some advanced features relative to synchronized, with the following three major items:

1. Wait can be interrupted: when the thread holding the lock does not release the lock for a long time, the waiting thread can choose to discard the wait, instead of handling other things, it is very useful for processing the synchronization block. While waiting for a mutex to be generated by synchronized, it is blocked and cannot be interrupted.

2, can achieve a fair lock: Multiple threads waiting for the same lock, must be in accordance with the order of the time to request the lock queue, and not fair lock does not guarantee this, in the lock release, any waiting for the lock thread has the opportunity to obtain the lock. Synchronized locks are not fair in the lock, Reentrantlock are not fair by default, but they can be constructed using Reentrantlock (ture) to require fair locks.

3. Locks can be bound to multiple conditions: The Reentrantlock object can bind multiple condition objects at the same time (name: conditional variable or conditional queue), and in synchronized, The Wait () and notify () or Notifyall () methods of the lock object can implement an implied condition, but if you want to associate with more than one condition, you have to add a lock extra, and Reentrantlock does not. You only need to call the Newcondition () method multiple times. And we can also use binding condition objects to determine which threads the current thread notifies (that is, other threads that are bound to the condition object).


can break lock

Reetrantlock has two types of locks: ignoring the interrupt lock and responding to the interrupt lock. Ignoring an interrupt lock, like a mutex implemented by synchronized, does not respond to interrupts, and response break locks can respond to interrupts.

If a thread A is executing the code in the lock, another thread B is waiting to acquire the lock, possibly because the wait time is too long, threads B does not want to wait, wants to deal with other things first, we can let it interrupt itself or interrupt it in another thread, if the reetrantlock provided is to ignore the interrupt lock, It will not ignore the interruption, but let thread B continue to wait, and if at this point Reetrantlock is providing a response interrupt lock, then it will handle the interrupt and let thread B give up waiting and turn to other things.

The general form of obtaining response interrupt locks is as follows:

	Reentrantlock lock = new Reentrantlock ();
	...........
	lock.lockinterruptibly ()//Get response Interrupt Lock
	try {
	      //Update object status
	      //catch exception, revert to original invariant constraint
	      if necessary//if there is return statement, put it here
	}finally{
		Lock.unlock ();        The lock must be released in the finally block
	

Here's a good sample code for analyzing interrupts (excerpt from the web)

When you interrupt the wait for a mutex with synchronized, it does not work, and the thread still waits, as in the following example:

public class Buffer {private Object lock;
    Public Buffer () {lock = this;
            public void Write () {synchronized (lock) {Long starttime = System.currenttimemillis ();
            System.out.println ("Start writing data to this buff ..."); for (;;) Impersonation takes a long time to process {if (System.currenttimemillis ()-StartTime > Int Eger.
                Max_value) {break;
        } System.out.println ("finally finished");
        } public void Read () {synchronized (lock) {System.out.println ("read data from this buff");

        } public static void Main (string[] args) {Buffer buff = new buffer ();
        Final Writer Writer = new Writer (buff);

        Final Reader reader = new reader (buff);
        Writer.start ();

        Reader.start (); New Thread (New Runnable () {@Override public void run () {Long START = System.currenttimemillis (); for (;;) {//wait 5 seconds to interrupt read if (System.currenttimemillis ()-Star
                        T > 5000) {System.out.println ("unequal, attempt interrupted");  Reader.interrupt ();
                    Attempt to interrupt read thread break;
        }}}). Start (); We expect the "read" thread to exit the waiting lock, but it backfired, once read this thread found itself not to get the lock,//began to wait, even if it died, it can not get the lock, because the write thread will take 2.1 billion seconds to complete t_t, even if we interrupt it,//it does not ring Should be, it seems really going to die. At this time, Reentrantlock gives a mechanism for us to respond to interrupts,//Let "read" can stretch, and bravely give up waiting for the lock.
    Let's rewrite the buffer class, called bufferinterruptibly, to break the cache.

    } class Writer extends Thread {private Buffer buff;
    Public Writer (Buffer buff) {this.buff = buff;
    @Override public void Run () {buff.write ();

    Class Reader extends Thread {private Buffer buff;
    Public Reader (Buffer buff) {this.buff = buff; } @OverrIDE public void Run () {buff.read ();//This is estimated to block System.out.println (read-end); }
}

The results of the implementation are as follows:

We waited a long time, and there was still no output, which explained that the reader thread's wait for the mutex was not interrupted, that is, the user's eating lock did not respond to the interrupt of the read thread. We then change the synchronized mutex in the above code to a Reentrantlock response interrupt lock, which is changed to the following code:

Import Java.util.concurrent.locks.ReentrantLock;

    public class Bufferinterruptibly {private Reentrantlock lock = new Reentrantlock ();
        public void Write () {lock.lock ();
            try {Long starttime = System.currenttimemillis ();
            System.out.println ("Start writing data to this buff ..."); for (;;) Impersonation takes a long time to process {if (System.currenttimemillis ()-StartTime > Int Eger.
                Max_value) {break;
        } System.out.println ("finally finished");
        finally {Lock.unlock ();    
        } public void Read () throws interruptedexception {lock.lockinterruptibly ()//Note here, you can respond to interrupts
        try {System.out.println ("read data from this buff");
        finally {Lock.unlock ();

        } public static void Main (String args[]) {bufferinterruptibly buff = new bufferinterruptibly (); Final Writer2 writer = New Writer2 (Buff);

        Final Reader2 reader = new Reader2 (buff);
        Writer.start ();

        Reader.start (); New Thread (New Runnable () {@Override public void run () {Long start = System.cur
                Renttimemillis (); for (;;)
                        {if (System.currenttimemillis ()-Start > 5000) {
                        System.out.println ("Ranged, attempt interrupted");  Reader.interrupt ();
                    Read operation break interrupted here;

    }}}). Start ();

    } class Reader2 extends Thread {private bufferinterruptibly buff;
    Public Reader2 (bufferinterruptibly buff) {this.buff = buff; @Override public void Run () {try {buff.read ();//can receive an interrupted exception to effectively exit} catch (in
        Terruptedexception e) {System.out.println ("I don't read it"); } System.out.println ("read End");

    } class Writer2 extends Thread {private bufferinterruptibly buff;
    Public Writer2 (bufferinterruptibly buff) {this.buff = buff;
    @Override public void Run () {buff.write (); }
    
}

The results of the implementation are as follows:

As can be seen from the results, an attempt to break the content in a catch statement block is also output, followed by "Read End", which indicates that the thread's wait for the mutex was interrupted, that is, the mutex was responding to the interrupt of the read thread.
The condition variable implements the collaboration between Threads
In the producer-consumer model , we use synchronized to implement mutual exclusion, and we use the object's Wait () and notify () or Notifyall () methods to implement the collaboration between threads. After Java 5, we can use the Reentrantlock lock with the await () and signal () or Signalall () methods on the condition object to achieve collaboration between threads. Newcondition () on the Reentrantlock object can get a condition object that can suspend a task (thread) by raising the await () method on the condition. Wake up a task by signal () on condition, or call Signalall () to wake up all the tasks that are suspended on this condition. In addition, if a fair lock is used, all tasks associated with condition in the Signalall () will acquire the lock in the form of a FIFO queue, and if a fair lock is not used, the task of acquiring the lock is random so that we can better control the order in which the tasks in the await state acquire the lock. Signalall () is a more secure way than Notifyall (). In addition, it can specify a task that wakes up with its own condition object.
The following changes the code in the producer-consumer model to a conditional variable, as follows:

Import java.util.concurrent.*;

Import java.util.concurrent.locks.*; Class info{//Definition information class private string name = "Name";//define the Name property to distinguish the private string content = "Content" from the name attribute of the set below;/Set	The semantic content attribute, in order to distinguish it from the following set's content attribute, open private Boolean flag = true;  
	Sets the flag bit, initially producing private lock lock = new Reentrantlock (); Private Condition Condition = Lock.newcondition ();
		Produces a condition object public void Set (String name,string content) {lock.lock ();
			try{while (!flag) {condition.await ();	} this.setname (name);
			Set name Thread.Sleep (300);	This.setcontent (content);	Set content flag = FALSE;
		Changing the sign position, indicating that the condition.signal () can be taken away;
		}catch (interruptedexception e) {e.printstacktrace ();
		}finally{Lock.unlock ();
		} public void Get () {lock.lock ();
			try{while (flag) {condition.await ();
			} thread.sleep (300);
			System.out.println (This.getname () + "-->" + this.getcontent ());	Flag = true;
		Changing the sign position, indicating that it can produce condition.signal (); }catCH (interruptedexception e) {e.printstacktrace ();
		}finally{Lock.unlock ();
	} public void SetName (String name) {this.name = name;
	public void SetContent (String content) {this.content = content;
	Public String GetName () {return this.name;
	Public String getcontent () {return this.content;		} class Producer implements runnable{//through the Runnable to achieve multithreading private info info = null;
	Save Info Reference Public Producer (info info) {this.info = info; public void Run () {Boolean flag = true;//define token bit for (int i=0;i<10;i++) {if (flag) {this.info.set ("name--1",	"Content--1");
			Set name flag = FALSE;
			}else{this.info.set ("Name--2", "Content--2");//Set name flag = TRUE;
	Class Consumer implements runnable{private info = null;
	Public Consumer (Info info) {this.info = info;
		public void Run () {for (int i=0;i<10;i++) {this.info.get (); }} public class threadcasedemo{public static void Main (String args[]) {Info info= new Info ();	Instantiate the Info object Producer Pro = new Producer (info);	Producer Consumer con = new Consumer (info);
		Consumer New Thread (PRO). Start ();
		After starting the producer thread, restart the consumer thread try{thread.sleep (500);
		}catch (interruptedexception e) {e.printstacktrace ();
	New Thread (Con). Start ();
 }
}
After execution, you can also get the following results: Name--1--> content--1
Name--2--> content--2
Name--1--> content--1
Name--2--> content--2
Name--1--> content--1
Name--2--> content--2
Name--1--> content--1
Name--2--> content--2
Name--1--> content--1
Name--2--> content--2
It is not clear from the above that the await (), signal (), Signalall () method with the condition variable has the advantage of implementing the collaboration between threads using the Wait (), notify (), Notifyall () method of the object. But it has obvious advantages when it comes to dealing with more complex multithreaded issues. Therefore, lock and condition objects are only necessary in more difficult multi-threaded problems.

Read and write lock

In addition, synchronized acquired mutexes are not only mutually exclusive, read-write operations, write operations, but also mutually exclusive reading operations, while reading operations will not bring data competition, so the read-read operation is also mutually exclusive, will degrade performance. In Java 5, read-write locks are provided, which separates read and write locks, making read-read operations not mutually exclusive, the general form of acquiring read locks and writing locks as follows:

Readwritelock rwl = new Reentrantreadwritelock ();	 
Rwl.writelock (). Lock ()  //Get write lock
Rwl.readlock (). Lock ()  //Acquire read lock
Read the lock to lock the read operation, write locks to lock the write operation, so that the write operation and write operations will be mutually exclusive, read and write operations will be mutually exclusive, but read and read operations will not be mutually exclusive.

The best time to use Reentrantlock is given in the Java Concurrent programming Practice:

When you need the following advanced features, you should use: Timed, polling and interruptible lock acquisition operations, fair queues, or not block-structured locks. Otherwise, please use synchronized.






Related Article

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.