Java concurrent package Lock implementation principle, java concurrent package lock

Source: Internet
Author: User
Tags lock queue

Java concurrent package Lock implementation principle, java concurrent package lock
Introduction and use of Lock

Lock is a thread synchronization tool introduced in java 1.5. It is mainly used to control shared resources under multiple threads. Essentially, Lock is only an interface (located in java \ util \ concurrent \ locks in the source code package). It contains the following methods:

// Try to get the lock. If the lock is obtained successfully, return; otherwise, the current thread void lock () is blocked; // try to get the lock. If the thread is interrupted before the lock is obtained successfully, the lock is discarded, throw an exception void lockInterruptibly () throws InterruptedException; // If the lock is obtained successfully, true is returned. Otherwise, false boolean tryLock () is returned. // try to obtain the lock, if the lock is obtained within the specified time, true is returned. Otherwise, false is returned. If the lock is interrupted before it is obtained, an exception boolean tryLock (long time, TimeUnit unit) throws InterruptedException is thrown; // release the lock void unlock (); // return the condition variables of the current lock. The conditional variables can be used to implement functions similar to notify and wait, A lock can have multiple Condition variables Condition newCondition ();

Lock has three implementation classes: ReentrantLock and ReentrantReadWriteLock. The other two are static internal classes ReadLock and WriteLock.

Usage: when multiple threads are used to access (mutually exclusive) shared resources, lock before access and unlock after access. The unlocked operation is recommended in the finally block.

Lock l = ...; // obtain a Lock object according to the constructor of the Lock interface class. lock (); // obtain the lock located outside the try block. try {// access the resource protected by this lock} finally
 {     l.unlock();}

Note: The lock is located outside the try block for resource access, especially when the lockInterruptibly method is used to lock the block. This is required to prevent the thread from being interrupted when obtaining the lock, in this case, you do not have to (and cannot) release the lock.

Try {l. lockInterruptibly (); // when the lock fails to be obtained, the unlock statement in the finally block will not be executed. try {// access the resource protected by this lock} finally {l. unlock () ;}} catch (InterruptedException e) {// TODO Auto-generated catch block e. printStackTrace ();}
The basic idea of implementing the Lock Interface

There are two essential elements for implementing the lock function. One is a variable that represents the (LOCK) State (we suppose 0 indicates no thread obtains the lock, and the other 1 indicates that the existing thread occupies the lock ), the other is the queue. nodes in the queue indicate the threads that are blocked due to the failure to obtain the lock. To solve the inconsistency of multi-thread cache in a multi-core processor, the state variables must be declared as voaltile type, and atomic and visibility must be ensured for certain operations on variables and queues that indicate State. Atomic and visibility operations are mainly implemented through methods in the Atomic package.

The general process in which the thread acquires the lock (the process of re-entry and attempt to obtain the lock is interrupted or timed out is not considered here)

1. Read the variable indicating the status

2. if the value of the state variable is 0, the current thread attempts to set the value of the variable to 1 (completed through the CAS operation ), when multiple threads set the variable value indicating the State from 0 to 1 at the same time, only one thread can succeed.

The thread will fail.

2.1 If successful, the lock is obtained,

2.1.1 if the thread is already in the queue, columns it (and changes the next node to the first node in the queue)

2.1.2If this thread is not included in the column, no queue maintenance is required.

Then the current thread returns from the lock method to access the shared resources.

2.2 If the thread fails, the current thread puts itself into the waiting (locked) queue and blocks itself. At this time, the thread is blocked in the lock method and no result is returned from this method.(After being awakenedLockMethod, and return to 1stStep to start again).

3. If the value of the variable indicating the status is 1, put the current thread into the waiting queue and block itself(After being awakenedLockMethod, and return to 1stStep to start again).

Note: wakeup does not indicate that the thread can run immediately, but that the thread is in the ready state and can run.

 

General process of thread release lock

1. The thread releasing the lock sets the value of the status variable from 1 to 0, and wakes upFirst NodeThe thread that releases the lock returns from the unlock method and continues executing the Code following the thread.

2. The wake-up thread (the first node of the queue) and the thread that is not in the queue and preparing to obtain the lock may compete to obtain the lock again.

Note: multiple threads may compete to obtain the lock at the same time, but only one thread can release the lock at a time. All nodes in the queue need the previous node to wake up the lock, for example, queue A <-B-<C, that is, B is awakened when A releases the lock, and B is awakened when B releases the lock.

 

Fair lock and unfair lock

Locks can be divided into fair locks and unfair locks, reentrant locks and non-reentrant locks (Introduction to ReentrantLock source code analysis ), the above process is actually an unfair lock acquisition and release process.

The fair lock strictly follows the first and last steps to obtain the lock, rather than the fair lock allows the queue to obtain the lock.

There are some differences in the process of obtaining a fair lock. When using a fair lock, a thread not only needs to determine whether the value of the variable in the current State is 0, it also determines whether there are other threads in the queue. If there are other threads in the queue, it means that the current thread needs to queue, perform the column entry operation, and block itself. If the queue is empty, in order to try to obtain the lock. For unfair locks, when the value of the variable indicating the status is 0, you can try to get the lock, regardless of whether the queue is empty, so as to realize the characteristics of the queue. Generally, the throughput of unfair locks is higher than that of fair locks. We usually use unfair locks.

Here, we need to explain under what circumstances, that is, the value of the variable indicating the lock status is 0 and other threads in the queue are still waiting to obtain the lock.

Assume there are three threads A, B, and C. Thread A is A running thread and holds the lock. There is a c thread in the queue, which is at the beginning of the queue. Now thread A wants to release the lock. The specific process operation can be divided into two steps:

1. Change the value of the variable indicating the lock status from 1 to 0,

2. The C thread is awakened. here we need to clarify two points:(1)The wake-up of the C thread does not mean that the C thread starts to execute. The C thread is in the ready state and needs to wait for CPU polling.(2)The C thread is not listed yet. The C thread must enter the running state and obtain the lock through competition before it can be listed.

If the C thread does not enter the running state at this time, and the B thread in the queue does not obtain the lock, B will find that although no thread currently holds the lock, however, the queue is not empty (the C thread is still in the queue). To satisfy the first-to-last-arrival features (B executes the lock acquisition operation after C), thread B cannot try to obtain the lock, instead, it performs the column import operation.

 

Basic Idea of implementing the Condition Interface

Condition is essentially an interface that contains the following methods:

// Let the thread enter the waiting for Notification status. before receiving the notification, you can end the waiting state through interruption and throw an exception.

Void await () throws InterruptedException;

// Wait for the thread to enter the status of the notification and cannot be interrupted.

Void awaitUninterruptibly ();

// Let the thread enter the pending notification Status, wait for timeout to end, and throw an exception

Long awaitNanos (long nanosTimeout) throws InterruptedException;

Boolean await (long time, TimeUnit unit) throws InterruptedException;

Boolean awaitUntil (Date deadline) throws InterruptedException;

// Switch a thread under the same waiting condition from the waiting notification Status to the waiting lock status

Void signal ();

// Convert all threads under the same waiting condition from the waiting notification blocking status to the waiting lock blocking status

Void signalAll ();

A Condition instance actually maintains a queue, and the nodes in the queue indicate the thread that calls the await method by itself because (some conditions are not met. The Condition interface has two important methods: The await method and the signal method. Before the thread calls this method, the thread must have obtained the lock attached to the Condition instance. There are two reasons for this: 1 for the await method, it will execute the release Lock operation internally, so you must obtain the lock before using 2 for the signal method, it is to avoid the (Queue) Out-of-column competition caused by multiple threads calling the singal method of the same Condition instance at the same time. The following is the execution process of the two methods.

Await method:

1. columnsCondition Queue (This is not a queue waiting for locks.)

2. Release the lock

3. blocking its own thread

------------ Run ----------- after being awakened -------------

4. Try to get the lock (when the thread is executed here, it is no longer in the condition queue,In the waiting (locked) queue, See the signal method)

4.1 successful, returned from the await method, the code behind the execution thread

4.2 failed, blocking yourself (wait until the previous node releases the lock to wake it up)

Note: The await method is called by its own thread. The thread is blocked in the await method and is not returned from the await method. When it is awakened, it continues to execute the Code following the await method. We can see that the await method has released the lock and tried to obtain the lock.

 

Signal method:

1. Retrieve the first node of the condition queue and put it at the end of the lock queue.

2. Wake up the thread corresponding to the node

Note: signal is called by other threads.

In the following example, we use lock and condition to print A line B statement first, and then thread A prints two statements (which cannot be interrupted), which end after ten alternation.

public class ConditionDemo {    volatile int key = 0;    Lock l = new ReentrantLock();    Condition c = l.newCondition();        public static  void main(String[] args){        ConditionDemo demo = new ConditionDemo();        new Thread(demo.new A()).start();        new Thread(demo.new B()).start();    }        class A implements Runnable{        @Override        public void run() {            int i = 10;            while(i > 0){                l.lock();                try{                    if(key == 1){                        System.out.println("A is Running");                        System.out.println("A is Running");                        i--;                        key = 0;                        c.signal();                    }else{                        c.awaitUninterruptibly();                                            }                                    }                finally{                    l.unlock();                }            }        }            }        class B implements Runnable{        @Override        public void run() {            int i = 10;            while(i > 0){                l.lock();                try{                    if(key == 0){                        System.out.println("B is Running");                        i--;                        key = 1;                        c.signal();                    }else{                        c.awaitUninterruptibly();                                            }                                    }                finally{                    l.unlock();                }            }        }        }}
Differences between Lock and synchronized

1. Both Lock locking and unlock are implemented by java code in combination with the native method (call the relevant methods of the operating system), and the process of locking and unlocking synchronize is managed by JVM.

2. When a thread uses synchronize to obtain the lock, if the lock is occupied by other threads, it can only be blocked until the lock is obtained successfully. The Lock provides more flexible methods such as timeout Lock and interruption, and provides an exit mechanism when the Lock is not obtained.

3. A lock can have multiple Condition instances, that is, multiple Condition queues, while synchronize only has one Condition queue. Likewise, Condition also provides flexible blocking methods, before receiving a notification, you can exit the condition queue by interrupting the thread and setting the waiting time limit.

4. synchronize only provides the exclusive mode for thread synchronization, while Lock can provide exclusive mode or shared mode.

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.