Lock--lock (top) in Java thread concurrency __java

Source: Internet
Author: User
Tags cas finally block joins mutex prev volatile java se
Lock Interface

Locks are used to control how multiple threads access shared resources, in general, a lock prevents multiple threads from accessing shared resources at the same time (but some locks allow multiple threads to access shared resources concurrently, such as read-write locks). Prior to the lock interface, the Java program was locked by the Synchronized keyword, and after Java SE 5, the Lock interface (and the associated implementation class) was added to implement the locking function, which provided a synchronization function similar to the SYNCHRONIZED keyword , it is only necessary to acquire and release the lock explicitly when using it. Although it lacks the convenience of implicitly acquiring a release lock (by synchronized block or method), it has the ability to acquire and release locks, interruptible access locks, and a number of synchronized keywords, such as timeout acquisition locks, that do not have synchronization characteristics.
The use of the Synchronized keyword implicitly acquires the lock, but it solidifies the acquisition and release of the lock, which means that it is first obtained and then released. Of course, this approach simplifies the management of synchronization, but extensibility does not show lock acquisition and release to the good. For example, for a scene, the hand of the lock acquisition and release, first obtain the lock a, and then obtain the lock B, when the lock B is obtained, release lock A at the same time acquire the lock C, when the lock C is obtained, then release B and obtain the lock D, and so on. In this scenario, the Synchronized keyword is not easy to implement, and lock is much easier to use.
The code for lock uses an example:

public class Locktest {public
    void Locktest () {
        lock lock = new Reentrantlock ();
        Lock.lock ();
        try{}
        catch (Exception t) {
        }finally{
            lock.unlock ();
        }
    }
}

The lock is freed in the finally block to ensure that the lock can eventually be freed after it has been acquired. Do not write the process of acquiring a lock in a try block, because if an exception occurs when acquiring a lock (the implementation of a custom lock), the exception is thrown and the lock is freed for no reason. Lock Interface Method

  void Lock ();

Gets the lock, and the call to the method thread acquires the lock, which is returned from the method when the lock is acquired

void Lockinterruptibly () throws interruptedexception;

An interruptible fetch lock differs from the lock () method in that it responds to interrupts in which the current thread can be interrupted in the acquisition of a lock

Boolean Trylock ();

Tries a non-blocking fetch lock, which is returned immediately after the method is invoked, returns true if it succeeds, or false

Boolean Trylock (long time, Timeunit unit) throws Interruptedexception;

Timeout gets the lock, and the current thread returns in the following cases
1. The current thread acquires the lock within the timeout range
2, the current thread is interrupted within the timeout range
3, timeout time end return false

void unlock ();

Release lock

Condition newcondition ();

Gets the wait notification component, which is bound to the current lock, that the current thread only acquires a lock in order to call the () method of the component, and the current thread releases the lock after the call. the difference between lock interface and synchronized

Attempt to acquire a lock non-blocking: the current thread attempts to acquire a lock, and if the lock is not acquired by another thread, the lock is successfully acquired and held
can be interrupted to acquire a lock: unlike synchronized, acquired locks can respond to interrupts, and when a thread that acquires a lock is interrupted, the interrupt exception is thrown and the lock is released
Timeout acquire lock: Get lock before time expires, return queue synchronizer If lock is not available until deadline

Queue Synchronizer Abstractqueuedsynchronizer (Synchronizer) is the basic framework for building locks or other synchronization components, which uses an INT member variable to represent the synchronization state, and completes the queue of resource fetching threads through the built-in FIFO queue. And the author of the contract (Doug Lea) expects it to be the basis for achieving most of the synchronization requirements. The main use of the Synchronizer is to inherit, the subclass by inheriting the Synchronizer and implementing its abstract methods to manage the synchronization state, in the implementation of the abstract method is unavoidable to change the synchronization state, then need to use the Synchronizer provided 3 methods (GetState (), SetState (int NewState) and compareandsetstate (int expect,int update) to operate because they ensure that state changes are safe. Subclasses recommend static internal classes that are defined as custom synchronization components. Synchronizer itself does not implement any synchronization interface, it is only defined a number of synchronization state acquisition and release methods for custom synchronization components to use, the Synchronizer can either support the exclusive access to the synchronization state, but also can support the sharing of the synchronization state, This makes it easy to implement different types of synchronization components (Reentrantlock, Reentrantreadwritelock, Countdownlatch, and so on). Synchronizer is the key to implement the lock (also can be any synchronous component), in the implementation of the lock aggregation Synchronizer, the use of Synchronizer to achieve the semantics of the lock. You can understand the relationship between the two: the lock is user-oriented, it defines the interface that the user interacts with the lock (such as allowing two threads to be accessed in parallel), hides the implementation details, and the Synchronizer is designed to implement the lock, simplifying the implementation of the lock, shielding synchronization state management, thread queuing, Wait and wake up the underlying operation. The lock and Synchronizer are a good way to isolate the areas of concern that users and implementations need. Queue Synchronizer Interface Description

The design of the

Synchronizer is based on the template method pattern. That is, the consumer needs to inherit the Synchronizer and override the specified methods, then combine the Synchronizer into the custom synchronization component's implementation and invoke the template methods provided by the Synchronizer, which will invoke the user-overridden method. When overriding a method specified by the Synchronizer, you need to use the following 3 methods provided by the Synchronizer to access or modify the synchronization state.
getState (): get current sync status
setstate (): set current sync status
compareandsetstate (int expect,int update) : Use the CAs to set the current state, which guarantees the atomic nature of the state setting
Synchronizer can override the method

To obtain the synchronization state exclusively, this method requires querying the current state and determining whether the synchronization state is expected, and then setting the synchronization state via CAs
protected boolean tryacquire (int arg) {
        throw new Unsupportedoperationexception ();
    }

Exclusive release of the sync lock, waiting for the thread to get the sync state to get to the sync state
protected boolean tryrelease (int arg) {
        throw new Unsupportedoperationexception ();
    }
Share Gets the sync state, returns greater than 0 to indicate success, whereas failure 
protected int tryacquireshared (int arg) {
        throw new unsupportedoperationexception ();
    }
Share-free synchronization state
protected Boolean tryreleaseshared (int arg) {
        throw new unsupportedoperationexception ();
    }
//Whether the current Synchronizer is occupied by threads in exclusive mode, typically this method represents whether the current thread is exclusive
protected Boolean isheldexclusively () {
        throw new Unsupportedoperationexception ();
    }

The template method provided by the Synchronizer
Exclusive lock is at the same time only one thread gets to the lock, while the other thread that acquires the lock can only wait in the synchronization queue, only the thread that acquires the lock releases the lock, and subsequent line Cheng can acquire the lock

Gets the synchronization state exclusively, if the current thread obtains the sync state successfully, it is returned by the method, or enters the synchronization queue, which calls the overridden//Tryaccquire method public final void acquire (int arg) {if
    (!tryacquire (ARG) && acquirequeued (Addwaiter (node.exclusive), Arg) selfinterrupt (); ///Same as the Acquire method, but the method responds to the interrupt, the current thread is not fetched to the synchronization state and enters the synchronization queue, and if the current thread is interrupted, the method//will throw a interruptedexception and return the public final void acquireinterruptibly (int arg) throws Interruptedexception {if (thread.interrupted ()) thro
        W new Interruptedexception ();
    if (!tryacquire (ARG)) doacquireinterruptibly (ARG); The timeout delay was added to the Acquireinterruptibly method, or True public final Boolean Tryacquirenanos if the current thread does not get a sync state in the timeout period returns false ARG, long nanostimeout) throws Interruptedexception {if (thread.interrupted ()) throw new
        Interruptedexception ();
            return Tryacquire (ARG) | |
    Doacquirenanos (ARG, nanostimeout); //share Gets the sync state, and if the current thread does not get the sync state, it will go into the synchronization queue, and the only difference from exclusive acquisition is that it can have//multiple threads at the same timeGets to the sync state public final void acquireshared (int arg) {if (tryacquireshared (ARG) < 0) doacquireshared (a
    RG); }//With acquireshared, just this method responds to interrupt public final void acquiresharedinterruptibly (int arg) throws Interruptedexcepti
        on {if (thread.interrupted ()) throw new Interruptedexception ();
    if (tryacquireshared (ARG) < 0) doacquiresharedinterruptibly (ARG); ////On acquireshared basis added Super Time public final Boolean tryacquiresharednanos (int arg, long nanostimeout) throws int
        erruptedexception {if (thread.interrupted ()) throw new Interruptedexception ();
            return tryacquireshared (ARG) >= 0 | |
    Doacquiresharednanos (ARG, nanostimeout);
            //Exclusive Release synchronization state, which wakes up the thread contained in the first node of the synchronization queue after releasing the synchronization state public final Boolean release (int arg) {if (Tryrelease (ARG)) {
            Node h = head;
      if (h!= null && h.waitstatus!= 0) unparksuccessor (h);      return true;
    return false; //Shared Release Sync state public Final Boolean releaseshared (int arg) {if (tryreleaseshared (ARG)) {doreleases
            Hared ();
        return true;
    return false; //Get a collection of threads waiting on the synchronization queue public final collection<thread> getqueuedthreads () {arraylist<thread> list = NE
        W arraylist<thread> ();
            for (Node p = tail p!= null; p = p.prev) {Thread t = p.thread;
        if (t!= null) List.add (t);
    } return list; }

See an example of an exclusive lock below

Package com.thread;
Import Java.util.concurrent.TimeUnit;
Import Java.util.concurrent.locks.AbstractQueuedSynchronizer;
Import java.util.concurrent.locks.Condition;
Import Java.util.concurrent.locks.Lock; public class Mutex implements Lock {//static inner class, custom Synchronizer @SuppressWarnings ("serial") private static class Sync ex Tends abstractqueuedsynchronizer{//is in the occupied State protected Boolean isheldexclusively () {return GE
        Tstate () = = 1; //When State ==0 acquire lock, update state=1 public boolean tryacquire (int acquires) {if (compareandsetstate (0, 1)
                ) {Setexclusiveownerthread (Thread.CurrentThread ());
            return true;
        return false; //Release lock, set state to 0 public boolean tryrelease (int release) {if (getState () = 0) throw new illegal
            Monitorstateexception ();
            Setexclusiveownerthread (NULL);
            SetState (0);
        return true; }
        //Returns a Condition, each Condition contains a Condition queue Condition newcondition () {return new Conditionobject ();
    };
    //Only need to move the action agent to sync on sync sync = new sync ();
    @Override public void Lock () {sync.acquire (1);
    @Override public void lockinterruptibly () throws Interruptedexception {sync.acquireinterruptibly (1);
    @Override public boolean Trylock () {return sync.tryacquire (1); @Override public boolean Trylock (long, Timeunit unit) throws Interruptedexception {return sync.try
    Acquirenanos (1, Unit.tonanos (time));
    @Override public void Unlock () {sync.release (1);
    @Override public Condition newcondition () {return sync.newcondition (); 
    public boolean islocked () {return sync.isheldexclusively (); 
    public Boolean hasqueuedthreads () {return sync.hasqueuedthreads (); }
}

In the preceding example, the exclusive lock mutex is a custom synchronization component that allows only one thread to occupy the lock at the same time. The mutex defines a static inner class that inherits the Synchronizer and implements an exclusive fetch and release synchronization state. In the Tryacquire (int acquires) method, if the CAS setting succeeds (the synchronization state is set to 1), the synchronization state is obtained, and in the tryrelease (the int releases method only resets the sync state to 0. Instead of dealing directly with the implementation of the internal Synchronizer when using a mutex, the user invokes the method provided by the mutex, taking the lock () method of the latch as an example in the implementation of the mutex, by simply calling the Synchronizer's template method acquire (int args) in the method implementation. When the current thread calls this method to obtain the sync state, it is added to the synchronization queue to wait, thus greatly reducing the threshold for implementing a reliable custom synchronization component. principle analysis of queue Synchronizer Queue Synchronization

The Synchronizer relies on internal synchronization queues (a FIFO bidirectional queue) to complete the synchronization state management, when the current thread acquires a sync state failure, the Synchronizer constructs information such as the current thread and the wait state into a node and joins it in the synchronization queue, blocking the current thread and, when the synchronization state is released, Wakes up the thread in the first node to try to get the sync state again.
Nodes in the synchronization queue are used to hold thread references, wait states, and the predecessor and successor nodes that failed to get the sync state

/** * Wait status * 1, cancelled, value 1, the node will not change if the waiting thread in the synchronization queue waits for a timeout or is interrupted to cancel the wait from the synchronization queue * 2, SIGNL, the value is-1, the successor node's thread is in the waiting state, and if the current node's thread releases the synchronization state or is canceled, it will notify the successor node, so that the successor node's thread runs * 3, CONDITION, the value is 2, the node is in the waiting queue, The node thread waits on the condition, and when another thread invokes the signal () method on condition, the node is transferred from the wait * queue to the synchronization queue, joined to the synchronization state fetch * 4, PROPAGATE, and the value is-3, representing the next shared sync
     State acquisition will be unconditionally spread * 5, INITIAL, the value of 0, the initial state/volatile int waitstatus;
     /** * Precursor node, when the node joined the synchronization queue is set, (tail added)/volatile node prev;
     /** * Successor nodes/volatile node next;

     /** * Gets the sync state thread/volatile thread thread; /** * waits for the successor node in the queue, and if the current node is shared, then the field will be a share constant, that is, the node type (exclusive, shared) and the successor node in the wait queue common to one field/node Nextwaiter; 

The

node is the basis for the synchronization queue, which has a first node (head) and tail node (tail), and a thread that does not successfully acquire the sync state will become the tail of the node joining the queue, as shown in the following figure in the basic structure of the synchronization queue.

The Synchronizer in the diagram above contains references to two node types, one pointing to the head node and the other pointing to the tail node. Imagine that when a thread succeeds in acquiring a sync state (or a lock), the other threads will not be able to get to the sync state, instead being constructed into nodes and joined to the synchronization queue, and the process of joining the queue must ensure thread safety, Therefore, the Synchronizer provides a way to set the tail node based on CAS: compareandsettail (node expect,nodeupdate), which needs to pass the end node and the current node "considered" by the current thread, only after the setting is successful The current node is formally associated with the previous tail node. The procedure in which the
Synchronizer joins a node in a synchronization queue is shown in the following illustration. The

Synchronization queue follows the FIFO, the first node is the node that obtains the synchronization state success, and the first node wakes up the successor node when the synchronization state is released, and the subsequent node sets itself as the home node when the synchronization state succeeds, as shown in the following illustration. The

sets the first node by obtaining a successful thread for the synchronization state, because only one thread can successfully get to the synchronization state, so the method of setting the header node does not need to be guaranteed by using CAs. It only needs to set the first node as the successor node of the original node and disconnect the next reference from the original node.

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.