We know that synchronized is an internal Java keyword. It is a heavyweight exclusive lock. The advantage of synchronized is that it is easy to use and does not need to manually release the lock. However,

Lock manually Lock, manually release the Lock;

One already use

ReentrantLock means ReentrantLock, and the method is previewed below

// Create an instance of ReentrantLock
ReentrantLock() 
 // Create a ReentrantLock with a given fairness policy
ReentrantLock(boolean fair)
// Query the number of locks held by the current thread
int getHoldCount(a) 
// Returns the thread that currently owns the lock, or null if the lock is not owned by any thread
protected Thread getOwner(a) 
// Returns a collection of threads that might be waiting to acquire the lock
protected Collection<Thread> getQueuedThreads(a) 
int getQueueLength(a) // Returns an estimate of the number of threads waiting to acquire this lock
 // Returns a collection of threads that might be waiting for a given condition related to this lock
protected Collection<Thread> getWaitingThreads(Condition condition)
// Returns the estimated number of threads waiting for a given condition associated with this lock
int getWaitQueueLength(Condition condition) 
// Query whether the given thread is waiting to acquire the lock
boolean hasQueuedThread(Thread thread) 
// Check whether some threads are waiting to acquire the lock
boolean hasQueuedThreads(a) 
// Queries whether there are threads waiting for a given condition related to this lock
boolean hasWaiters(Condition condition) 
// Returns true if the lock's fair setting is true
boolean isFair(a) 
// Check whether the current thread holds the lock
boolean isHeldByCurrentThread(a)
// Check whether this lock is held by any thread
boolean isLocked(a)
/ / acquiring a lock
void lock(a)
// If the current thread is not interrupted, the lock is acquired.
void lockInterruptibly(a) 
// Return the Condition instance used with this Lock instance
Condition newCondition(a) 
 // The lock is acquired only if it is not held by another thread when called
boolean tryLock(a)
// If the lock is not held by another thread within a given wait time and the current thread is not interrupted, the lock is acquired
boolean tryLock(long timeout, TimeUnit unit)
// Release the lock
void unlock(a) 
Copy the code

1.1Lock and unLock Simple Usage

Use lock() to add locks, unlock() to release locks;

public class RLook {

    private Lock lock = new ReentrantLock();

    private void testLock(a){
        / / lock
        lock.lock();
        // Execute business logic
        for (int i=0; i<8; i++){
            System.out.println(i+"= = ="+Thread.currentThread().getName());
        }
        / / releases the lock
        lock.unlock();
    }

    public static void main(String[] args) {
        / / thread 1
        RLook rLook = new RLook();
        new Thread(()-> {
            rLook.testLock();
        }).start();
        / / thread 2
        newThread(()-> { rLook.testLock(); }).start(); }}Copy the code

The output is as follows, which should be printed in groups between different threads;

0===Thread-0
1===Thread-0
2===Thread-0
3===Thread-0
4===Thread-0
5===Thread-0
6===Thread-0
7===Thread-0
0===Thread-1
1===Thread-1
2===Thread-1
3===Thread-1
4===Thread-1
5===Thread-1
6===Thread-1
7===Thread-1
Copy the code

1.2 Correct Usage of Lock and unLock

The above code has a disadvantage. If an exception occurs while executing the business code, a deadlock will occur. Therefore, we usually place the business code ina try{}, catch{} block, and use finally to release the lock.

The code format should be as follows

Lock lock =newReentrantLock(); . lock.lock();try{
    // Process tasks
}catch(Exception ex){
     
}finally{
    lock.unlock();   / / releases the lock}...Copy the code

1.3 Source point of view to explain Lock

public interface Lock {
	/ / lock
    void lock(a);
    / / the interrupt
    void lockInterruptibly(a) throws InterruptedException;
    // Try to get the lock
    boolean tryLock(a);
    // Try to get the lock
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    / / releases the lock
    void unlock(a);
    // Signal notification
    Condition newCondition(a);
}
Copy the code
  • The lock() method is used to obtain the lock. If the lock has been acquired by another thread, the lock wait occurs. The Lock must be released manually, and it is not automatically released in the event of an exception, so it needs to be placed ina try,cath block, and finally released.

  • The tryLock() method is used to indicate an attempt to acquire the lock, returning true on success and false on failure;

  • The tryLock(long time, TimeUnit Unit) method is similar to the tryLock() method except that you can set the lock wait time.

Lock lock=newReentrantLock(); .if(lock.tryLock()) {
     try{
         // Business logic
     }catch(Exception ex){
         
     }finally{
     / / releases the locklock.unlock(); }}else {
    // No lock logic is obtained
}
Copy the code
  • LockInterruptibly () is an interrupt method that throws an interrupt exception in response to an interrupt (the interrupted thread’s waiting state) if the thread is in the lock acquisition state while the lock is being acquired. In other words, when two threads simultaneously acquire A lock using lock.lockinterruptibly (), if thread A obtains the lock while threadB is waiting, calling threadb.interrupt () on threadB interrupts the wait state of threadB.
  • Condition class is a new API of JDK5, which can realize multi-way notification function. A Lock object can create multiple conditions, and flexibly realize different thread notification function by injecting different conditions. However, synchronized wait(), notify(), notify All() notification mechanism is random can not realize selective notification function;

1.4 use lockInterruptibly

public class InterruptTest {

    private Lock lock = new ReentrantLock();

    public void interrupt(a) throws InterruptedException {
        lock.lockInterruptibly();
        try {
            System.out.println(Thread.currentThread().getName() + "Got the lock.");
            long startTime = System.currentTimeMillis();

            for(; ;) {if (System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
                    break; }}finally {
            lock.unlock();
            System.out.println(Thread.currentThread().getName() + "Release lock"); }}public static void main(String[] args) {
        / / thread 1
        InterruptTest rLook = new InterruptTest();
        Thread threadA = new Thread(() -> {
            try {
                rLook.interrupt();
            } catch (InterruptedException e) {
                System.out.println("Thread A interrupts."); e.printStackTrace(); }});/ / thread 2
        Thread threadB = new Thread(() -> {
            try {
                rLook.interrupt();
            } catch (InterruptedException e) {
                System.out.println("Thread B interrupts."); e.printStackTrace(); }}); threadA.setName("A");
        threadB.setName("B");
        threadA.start();
        threadB.start();
        try {
            Thread.sleep(2000);
        } catch(InterruptedException e) { e.printStackTrace(); } threadB.interrupt(); }}Copy the code

The output result is as follows: Thread A acquires the lock through the interrupt, thread B acquires the lock through the interrupt and is in the waiting state.

A gets the lock thread and B interruptsCopy the code

1.5 Use Condition to realize wait notification mechanism

The main methods of the Coedition interface are as follows

 // Causes the current thread to wait until it receives a signal or is interrupted.
 void await(a) 
 // Causes the current thread to wait until it receives a signal, is interrupted, or reaches the specified wait time.
 boolean await(long time, TimeUnit unit) 
 // Causes the current thread to wait until it receives a signal, is interrupted, or reaches the specified wait time.
 long awaitNanos(long nanosTimeout) 
 // Causes the current thread to wait until it receives a signal.
 void awaitUninterruptibly(a) 
 // Causes the current thread to wait until it receives a signal, is interrupted, or reaches a specified deadline.
 boolean awaitUntil(Date deadline) 
 // Wake up a waiting thread.
 void signal(a)  
 // Wake up all waiting threads.
 void signalAll(a)  
Copy the code

The await notification mechanism is implemented with the await() method and signal() method of Condition.

public class ConditionTest {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    // The thread waits
    private void await(a){
        try {
            / / lock
            lock.lock();
            System.out.println("Await time is :"+System.currentTimeMillis());
            / / wait for
            condition.await();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            / / releases the locklock.unlock(); }}// Thread wake up
    public void signal(a) {
        lock.lock();
        try {
            System.out.println("Signal time is" + System.currentTimeMillis());
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public static void main(String[] args) throws InterruptedException {
        / / thread 1
        ConditionTest rLook = new ConditionTest();
        Thread thread = new Thread(() -> {
            rLook.await();
        });
        thread.start();
        //
        thread.sleep(3000);
        //rLook.signal(); }}Copy the code

Output The following output is displayed

The await time is 1606707478993. The signal time is 1606707481993Copy the code

Multiple Condition usage modes

When multiple conditions are used, they do not interfere with each other. Threads A and B using different Condition will enter the wait state. Only when the singal of the corresponding Condition is used will the corresponding thread be awakened.

public class MultiCondition {

    private Lock lock = new ReentrantLock();

    private Condition conditionA = lock.newCondition();
    private Condition conditionB = lock.newCondition();

    // The thread waits
    private void awaitA(a){
        try {
            / / lock
            lock.lock();
            System.out.println("Thread"+Thread.currentThread().getName()+"----await time is :"+System.currentTimeMillis());
            / / wait for
            conditionA.await();
            System.out.println("Thread"+Thread.currentThread().getName()+"Carry on");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            / / releases the locklock.unlock(); }}// The thread waits
    private void awaitB(a){
        try {
            / / lock
            lock.lock();
            System.out.println("Thread"+Thread.currentThread().getName()+"----await time is :"+System.currentTimeMillis());
            / / wait for
            conditionB.await();
            System.out.println("Thread"+Thread.currentThread().getName()+"Carry on");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            / / releases the locklock.unlock(); }}// Thread wake up
    public void signalAllA(a) {
        lock.lock();
        try {
            System.out.println("Thread"+Thread.currentThread().getName()+"----signal time is" + System.currentTimeMillis());
            conditionA.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}// Thread wake up
    public void signalAllB(a) {
        lock.lock();
        try {
            System.out.println("Thread"+Thread.currentThread().getName()+"----signal time is" + System.currentTimeMillis());
            conditionB.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public static void main(String[] args) throws InterruptedException {
        / / thread 1
        MultiCondition rLook = new MultiCondition();
        Thread threadA = new Thread(() -> {
            rLook.awaitA();
        });
        threadA.setName("A");
        threadA.start();
        Thread threadB = new Thread(() -> {
            rLook.awaitB();
        });
        threadB.setName("B");
        threadB.start();
        //
        Thread.sleep(3000);
        //rLook.signalAllA(); }}Copy the code

Condition A is awakened, while Condition B is still waiting.

Thread A----await time :1606708895650 Thread B----await time :1606708895650 Thread main----signal time: 1606708898651 Thread A continuesCopy the code

1.6 Use Condition to realize the production and consumption mode

public class ConsumerProductCondition {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    private Boolean has = false;

    / / production
    private void set(a){
        try {
            / / lock
            lock.lock();
            while (has == true){
                condition.await();
            }
            System.out.println("Production of GG");
            has = true;
            // Wake up a thread
            condition.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            / / releases the locklock.unlock(); }}/ / consumption
    private void get(a){
        try {
            / / lock
            lock.lock();
            while (has == false){
                condition.await();
            }
            System.out.println("MM consumption.");
            has = false;
            // Wake up a thread
            condition.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            / / releases the locklock.unlock(); }}public static void main(String[] args) throws InterruptedException {
        / / thread 1
        ConsumerProductCondition rLook = new ConsumerProductCondition();
        Thread threadA = new Thread(() -> {
            for (int i = 0; i < 200; i++) { rLook.set(); }});/ / thread 2
        Thread threadB = new Thread(() -> {
            for (int i = 0; i < 200; i++) { rLook.get(); }}); threadA.start(); threadB.start(); }}Copy the code

Alternate output printing

Production GG consumption MM production GG consumption MM production GG consumption MM production GG consumption MM production GG consumption MM.....Copy the code

1.7 Fair and Unfair Locks

Lock Locks are classified into fair locks and unfair locks. Fair locks are acquired through the sequence of thread locking, while unfair locks are acquired through preemption.

Fair lock capture example

public class FairLockTest {


    private Lock lock = new ReentrantLock(true);

    private void testLock(a){
        / / lock
        lock.lock();
        // Execute business logic
        System.out.println("Thread"+Thread.currentThread().getName()+"Lock obtained");
        / / releases the lock
        lock.unlock();
    }

    public static void main(String[] args) {
        / / thread 1
        FairLockTest rLook = new FairLockTest();


        for (int i = 0; i <100 ; i++) {
            / / thread 2
            Thread thread = new Thread(() -> {
                rLook.testLock();
            });
            thread.setName(""+i); thread.start(); }}}Copy the code

The output is basically orderly

Thread 0 has obtained the lock thread. 1 has obtained the lock thread. 2 Has obtained the lock thread. 3 Has obtained the lock thread. 4 Has obtained the lock threadCopy the code

The following is an example of an unfair lock

public class FairLockTest {


    private Lock lock = new ReentrantLock(false);

    private void testLock(a){
        / / lock
        lock.lock();
        // Execute business logic
        System.out.println("Thread"+Thread.currentThread().getName()+"Lock obtained");
        / / releases the lock
        lock.unlock();
    }

    public static void main(String[] args) {
        / / thread 1
        FairLockTest rLook = new FairLockTest();


        for (int i = 0; i <100 ; i++) {
            / / thread 2
            Thread thread = new Thread(() -> {
                rLook.testLock();
            });
            thread.setName(""+i); thread.start(); }}}Copy the code

The output result is basically unordered

Thread 0 obtains the lock thread. 1 Obtains the lock thread. 7 Obtains the lock thread. 2 Obtains the lock thread. 10 Obtains the lock threadCopy the code

Two ReentrantReadWriteLock use

ReentrantLock and synchronized are exclusive locks with high security, but relatively low efficiency. The ReentrantReadWriteLock read-write lock provides readLock() and writeLock() to obtain read and write locks. Read locks are not mutually exclusive (therefore, read locks are also shared). Read locks Write locks are mutually exclusive, and write locks write locks are mutually exclusive.

2.1 read lock

Read lock sample

public class ReadLockTest {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private void testLock(a){
        / / lock
        lock.readLock().lock();
        // Execute business logic
        System.out.println("Thread"+Thread.currentThread().getName()+"Lock obtained"+System.currentTimeMillis());
        // Sleep to ensure that thread A is still locked when thread B comes in
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        / / releases the lock
        lock.readLock().unlock();
    }

    public static void main(String[] args) {
        / / thread 1
        ReadLockTest rLook = new ReadLockTest();
        / / thread 2
        Thread threadA = new Thread(() -> {
            rLook.testLock();
        });
        threadA.setName("A");
        Thread threadB = new Thread(() -> {
            rLook.testLock();
        });
        threadB.setName("B"); threadA.start(); threadB.start(); }}Copy the code

Thread A and thread B acquire the lock at about the same time

Thread B has obtained the lock 1606723001641 Thread A has obtained the lock 1606723001641Copy the code

2.2 write lock

Write lock sample

public class WriteLockTest {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private void testLock(a){
        / / lock
        lock.writeLock().lock();
        // Execute business logic
        System.out.println("Thread"+Thread.currentThread().getName()+"Lock obtained"+System.currentTimeMillis());
        // Sleep to ensure that thread A is still locked when thread B comes in
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        / / releases the lock
        lock.writeLock().unlock();
    }

    public static void main(String[] args) {
        / / thread 1
        WriteLockTest rLook = new WriteLockTest();
        / / thread 2
        Thread threadA = new Thread(() -> {
            rLook.testLock();
        });
        threadA.setName("A");
        Thread threadB = new Thread(() -> {
            rLook.testLock();
        });
        threadB.setName("B"); threadA.start(); threadB.start(); }}Copy the code

The output is essentially 2 seconds off, meaning that after thread A acquires the lock, thread B must wait for thread A to release the lock before it acquires the lock

Thread A obtains the lock 1606723216664 Thread B obtains the lock 1606723218664Copy the code

2.3 read-write lock

Read/write lock example

public class ReadAndWriteTest {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private void writeLock(a){
        / / lock
        lock.writeLock().lock();
        // Execute business logic
        System.out.println("Thread"+Thread.currentThread().getName()+"Get write lock"+System.currentTimeMillis());
        // Sleep to ensure that thread A is still locked when thread B comes in
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        / / releases the lock
        lock.writeLock().unlock();
    }

    private void readLock(a){
        / / lock
        lock.readLock().lock();
        // Execute business logic
        System.out.println("Thread"+Thread.currentThread().getName()+"Get read lock"+System.currentTimeMillis());
        / / releases the lock
        lock.readLock().unlock();
    }

    public static void main(String[] args) {
        / / thread 1
        ReadAndWriteTest rLook = new ReadAndWriteTest();
        / / thread 2
        Thread threadA = new Thread(() -> {
            rLook.writeLock();
        });
        threadA.setName("A");
        Thread threadB = new Thread(() -> {
            rLook.readLock();
        });
        threadB.setName("B"); threadA.start(); threadB.start(); }}Copy the code

The output is as follows, exactly 2 seconds off; When thread A obtains the write lock, thread B must wait for thread A to release the write lock before obtaining the read lock.

Thread A has obtained the write lock 1606724500217 Thread B has obtained the read lock 1606724502217Copy the code

Three summary

From this article, we have a general idea of how common locks are used;

  • ReentrantLock and synchronized are exclusive locks and reentrant locks. Exclusive locks we all know, but reentrant locks,

When thread A obtains the lock and enters methodA, it does not need to obtain the lock again when it calls methodB.

class Test{
    public synchronized void methodA() {
        methodB();
    }
     
    public synchronized void methodB() {
         
    }
}
Copy the code
  • Synchronized is not a breakable Lock, whereas Lock is a breakable Lock. If thread A is executing the code in the lock and thread B waits too long to process other services, the lock acquired by thread B is called interruptible.

  • Fair locks Try to acquire locks in the order they were requested. For example, if there are multiple threads waiting for a lock, when the lock is released, the thread that waited the longest (the thread that requested it first) will get the lock. This is a fair lock. The unfair lock acquisition method is completely random, preemption mode;

  • Read/write lock is generally used to operate files, as long as the write lock is an exclusive lock; Read locks are shared locks;

For more information about other locks, read the Concurrent programming series previously published by Knowledge Seeker

Welcome to my public account: knowledge seeker, send original PDF, face book, open source system