Chapter 4 use of Lock in java multithreaded programming core technology

Knowledge points:

  1. Use of ReentrantLock class
  2. Use of ReentrantReadWriteLock class

4.1 use ReentrantLock class

ReentrantLock not only can achieve the same effect as synchronized, but also has sniffer lock, multi branch notification and so on.

package demo14;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    private Lock lock = new ReentrantLock();

    public void testMethod(){
        lock.lock();
        try {
            for (int i = 0; i <5 ; i++) {
                System.out.println("Thread name"+Thread.currentThread().getName()+"   "+(i+1));
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

package demo14;

public class MyThread extends Thread {

    private MyService myService;

    public MyThread(MyService myService){
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.testMethod();
    }
}

package demo14;

public class Run {

    public static void main(String[] args) {

        MyService myService = new MyService();
        MyThread myThread1 = new MyThread(myService);
        MyThread myThread2 = new MyThread(myService);
        MyThread myThread3 = new MyThread(myService);
        MyThread myThread4 = new MyThread(myService);
        myThread1.start();
        myThread2.start();
        myThread3.start();
        myThread4.start();
    }

}

4.1.3 use Condition to realize waiting for notification

package demo15;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    private Lock lock = new ReentrantLock();
    private Condition condition =lock.newCondition();

    public void await(){
        lock.lock();
        try {
            System.out.println("await time "+System.currentTimeMillis());
            //wait for
            condition.await();
            System.out.println("await Below ");

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signal(){
        lock.lock();
        try {
            System.out.println("signal time "+System.currentTimeMillis());
            //notice
            condition.signal();
            System.out.println("signal Below ");

        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

}
package demo15;


public class MyThread extends Thread {

    private MyService myService;

    public MyThread(MyService myService){
        this.myService = myService;
    }

    @Override
    public void run() {
        myService.await();
    }
}

package demo15;

public class Run {
    public static void main(String[] args) {

        try {
            MyService myService = new MyService();
            MyThread myThread1 = new MyThread(myService);
            myThread1.start();
            Thread.sleep(5000);

            myService.signal();
        }catch (Exception ex){
            ex.printStackTrace();
        }

    }

}

4.1.4 using multiple conditions to realize some threads of notification

package demo16;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyService {

    private Lock lock = new ReentrantLock();

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

    public void awaitA(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" awaitA =========== Get into lock lock");
            conditionA.await();
            System.out.println(Thread.currentThread().getName()+" awaitA =========== Wait for the end...");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void awaitB(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" awaitB =========== Get into lock lock");
            conditionB.await();
            System.out.println(Thread.currentThread().getName()+" awaitB =========== Wait for the end...");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signalAll_B(){

        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+" signalAll =========== Get into lock lock");
            conditionB.signal();
            conditionA.signal();
            System.out.println(Thread.currentThread().getName()+" signalAll =========== Notice over...");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

package demo16;

public class ThreadA extends Thread {

    private MyService myService;

    public ThreadA (MyService myService){

        this.myService = myService;
    }

    @Override
    public void run() {

        myService.awaitA();
    }
}

package demo16;

public class ThreadB extends Thread {

    private MyService myService;

    public ThreadB(MyService myService){

        this.myService = myService;
    }

    @Override
    public void run() {

        myService.awaitB();
    }
}

package demo16;

public class Run {

    public static void main(String[] args) {

        MyService myService = new MyService();
        ThreadA threadA = new ThreadA(myService);
        threadA.setName("A");
        threadA.start();
        ThreadB threadB = new ThreadB(myService);
        threadB.setName("B");
        threadB.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myService.signalAll_B();
    }
}


In this way, the thread specified in the wake-up part is realized.

4.1.5 fair lock and unfair lock

Fair lock:
Indicates that the order in which threads acquire locks is allocated according to the order in which threads are locked, that is, first come first get locks.
Unfair lock:
It is a preemptive mechanism to acquire locks. It is random to acquire locks. Unlike fair locks, first come locks do not have to be acquired first. It is likely that some threads will not get locks all the time.
Setting method:
Use the following construction method to create a mutually exclusive lock, true for fair lock, false for unfair lock, and the default is false.

The cases are as follows:

package demo17;

import java.util.concurrent.locks.ReentrantLock;

public class Service  {

    private ReentrantLock lock;

    public Service(boolean isFair){

        //If isFair is true, fair lock is false, unfair lock is false by default
        lock = new ReentrantLock(isFair);
    }

    public void serviceMethod(){

        lock.lock();
        try {
            System.out.println("thread"+Thread.currentThread().getName()+" Get lock");
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

package demo17;

public class RunFair {

    public static void main(String[] args) {

        Service service = new Service(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("thread"+Thread.currentThread().getName()+"Run");
                service.serviceMethod();
            }
        };

        Thread[] threads = new Thread[100];
        for (int i = 0; i <100 ; i++) {
            threads[i] = new Thread(runnable);
        }
        for (int i = 0; i <100 ; i++) {
            threads[i].start();
        }
    }
}

4.1.6 simple description of several methods:

getHoldCount() returns the number of times the current thread called lock().
getQueueLength() returns the thread waiting for the current lock.
getWaitQueueLength(Condition condition) returns the number of threads executing wait() of the current condition.
hasQueuedThread(Thread thread) queries whether the specified thread is waiting to acquire the lock.
hasQueuedThreads() queries whether a thread is waiting to acquire the lock.
hasWaiters(Condition condition) queries whether a thread is waiting for a condition condition related to this lock.
isFair() determines whether the lock is fair.
Isheldebycurrentthread() queries whether the current thread holds this lock.
isLocked() queries whether the lock is held by any thread.
Lockinterruptability() gets the lock if the current thread is not interrupted, and an exception occurs if it has been interrupted.
tryLock() acquires the lock only if it is not held by another thread at the time of the call.
tryLock(long timeout, TimeUnit unit) if the lock is not held by another thread within a given time, and the current thread has not been interrupted, the lock is acquired.

4.1.7 use Condition to implement sequential execution.

The Condition object can be used to sort the business executed by a thread.

package demo18;

public class F {

    volatile public static int count = 1;
}

package demo18;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Run {

    volatile private static int count =1;

    private  static ReentrantLock lock = new ReentrantLock();
    final private  static Condition conditionA = lock.newCondition();
    final private  static Condition conditionB = lock.newCondition();
    final private  static Condition conditionC = lock.newCondition();

    public static void main(String[] args) {

        Thread threadA = new Thread(() -> {
            lock.lock();
            try {
                while (count!=1){
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadA"+ (i+1));
                }
                count = 2;
                conditionB.signalAll();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }
        });

        Thread threadB = new Thread(() -> {
            lock.lock();
            try {
                while (count!=2){
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadB"+ (i+1));
                }
                count = 3;
                conditionC.signalAll();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }

        });

        Thread threadC = new Thread(() -> {
            lock.lock();
            try {
                while (count!=3){
                    conditionA.await();
                }
                for (int i = 0; i < 3; i++) {
                    System.out.println("ThreadC"+ (i+1));
                }
                count = 1;
                conditionA.signalAll();
            }catch (Exception ex){
                ex.printStackTrace();
            }finally {
                lock.unlock();
            }

        });
        Thread[] aArray = new Thread[5];
        Thread[] bArray = new Thread[5];
        Thread[] cArray = new Thread[5];
        for (int i = 0; i <5 ; i++) {

            aArray[i] = new Thread(threadA);
            bArray[i] = new Thread(threadB);
            cArray[i] = new Thread(threadC);
            aArray[i].start();
            bArray[i].start();
            cArray[i].start();
        }
    }

}

4.2 use ReentrantReadWriteLock class

ReentrantLock has a completely exclusive effect. That is to say, only one thread accesses the lock code at the same time. Although it ensures the thread safety of instance variables, it is inefficient.
There are two locks, read lock and write lock. The lock of read operation is called shared lock, and the lock of write operation is called exclusive lock. In other words, threads are not mutually exclusive when reading, but they are mutually exclusive as long as there are write operations involved.

4.2.1 reading sharing

package demo19;

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){

        lock.readLock().lock();
        try {
            System.out.println("Get read lock"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }
}

package demo19;

public class ThreadA  extends Thread{

    Service service;
    public ThreadA (Service service){

        this.service = service;
    }
    @Override
    public void run() {

        service.read();
    }
}

package demo19;

public class ThreadB extends Thread{

    Service service;
    public ThreadB(Service service){

        this.service = service;
    }
    @Override
    public void run() {

        service.read();
    }
}

package demo19;

public class Run {

    public static void main(String[] args) {

        Service service = new Service();
        ThreadA threadA = new ThreadA(service);
        threadA.setName("A");
        ThreadB threadB = new ThreadB(service);
        threadB.setName("B");
        threadA.start();
        threadB.start();
    }
}


The figure above shows the code of two threads entering the lock at the same time.

4.2.2 write mutex

package demo20;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void write(){

        lock.writeLock().lock();
        try {
            System.out.println("Get write lock"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
}


The effect of using write lock code lock.writeLock() is to allow only one thread to execute lock() code at the same time.

4.2.2 read write mutex

package demo21;

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void read(){

        lock.readLock().lock();
        try {
            System.out.println("Get read lock"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
    }

    public void write(){

        lock.writeLock().lock();
        try {
            System.out.println("Get write lock"+Thread.currentThread().getName() +" "+System.currentTimeMillis());
            Thread.sleep(10000);
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
}

The two threads then call separately.

Conclusion:
In this learning, you can completely replace the keyword synchronized with Lock object, which has many functions that synchronized does not have.

157 original articles published, 75 praised, 150000 visitors+
Private letter follow

Tags: Java

Posted on Thu, 16 Jan 2020 07:59:44 -0500 by teongkia