Multithreaded Programming Learning Four (Use of Lock)

1. Preface

This paper introduces the use of Lock object in Java5, which can also achieve synchronization effect, and is more convenient and flexible to use, mainly including the use of ReentrantLock class and the use of ReentrantReadWriteLock class.

2. Using the ReentrantLock class

1. In java multi-threading, synchronized keywords can be used to synchronize and mutually exclusive threads, but the new ReentrantLock in JDK1.5 also achieves the same effect and is more powerful in terms of extensions, such as sniffing locks, multi-branch notifications, and more flexible in use than synchronized.

2. Threads calling lock.lock() code have an Object Monitor, which means that locks hold object locks and depend on the existence of instances of that class.

public class MyService {
    private Lock lock=new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+(i+1));
        }
        lock.unlock();
    }
}

3. The keyword synchronized, combined with the wait() and notify()/notifyAll() methods, implements the wait/notification mode, and the ReentrantLock class can do the same, but with the help of the Condition object.

The wait() method in the Object class is equivalent to the await() method in the Condition class
The wait(long timeout) method in the Object class is equivalent to the await(long time, TimeUnit unit) method in the Condition class
The notify() method in the Object class is equivalent to the signal() method in the Condition class
The notifyAll() method in the Object class is equivalent to the signalAll() method in the Condition class
public class Myservice {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();

    //wait for
    public void waitMethod(){
        try {
            lock.lock();
            System.out.println("A");
            condition.await();//Called Condition Of await The wait method also needs to be in the synchronization method, otherwise an error will be reported
            System.out.println("B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    //awaken
    public void signal(){
        try {
            lock.lock();
            System.out.println("Start waking up now...");
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}

4. Use multiple Condition objects to implement selective notifications between threads.

public class MyService {
    private Lock lock=new ReentrantLock();
    //By defining multiple Condition Implement selective notifications,You can wake up a specified kind of thread, which is
    //A convenient way to control the behavior of some threads
    private Condition conditionA=lock.newCondition();
    private Condition conditionB=lock.newCondition();

    public void awaitA(){
        try {
            lock.lock();
            System.out.println("awaitA begin");
            conditionA.await();
            System.out.println("awaitA end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void awaitB(){
        try {
            lock.lock();
            System.out.println("awaitB begin");
            conditionB.await();
            System.out.println("awaitB end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signalA(){
        try {
            lock.lock();
            System.out.println("Start waking up now awaitA");
            conditionA.signalAll();
        }finally {
            lock.unlock();
        }
    }

    public void signalB(){
        try {
            lock.lock();
            System.out.println("Start waking up now awaitB");
            conditionB.signalAll();
        }finally {
            lock.unlock();
        }
    }
}
public class Run
{
    public static void main(String[] args) throws InterruptedException
    {
        MyService myService=new MyService();
        Thread threadA=new Thread(){
            @Override
            public void run()
            {
                super.run();
                myService.awaitA();
            }
        };

        Thread threadB=new Thread(){
            @Override
            public void run()
            {
                super.run();
                myService.awaitB();
            }
        };

        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        myService.signalA();
        Thread.sleep(1000);
        myService.signalB();

    }
}

5. Fair locks and unfair locks

Fair Lock: Indicates that the order in which threads acquire locks is allocated in the order in which threads lock, i.e. FIFO FIFO FIFO FIFO FIFO FIFO FIFO FIFO FIFO first in first out.
Unfair Lock: A preemptive mechanism to acquire locks is to acquire locks randomly, unlike fair locks, which do not necessarily acquire locks first in the first place. This way, some threads may not be able to acquire locks all the time, resulting in unfair results.
public class Service {
    private Lock lock;

    public Service(boolean isFair)
    {
        //Create a fair lock this way(true)And unfair locks(false)
        lock=new ReentrantLock(isFair);
    }

    public void methodA(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"Running");
        }finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args)
    {
        final Service service=new Service(true);
        Runnable runnable=new Runnable() {
            @Override
            public void run()
            {
             service.methodA();
            }
        };

        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++){
            threads[i]=new Thread(runnable);
            threads[i].setName("thread"+(i+1));
            threads[i].start();
        }
    }
}

6. Introduction of ReentrantLock's common methods

(1) int getHoldCount() queries the number of times the current thread holds this lock, that is, the number of lock method calls in the thread.

(2) int getQueueLength() returns the estimated number of threads waiting for this Lock, such as five threads, one thread holding the Lock for execution, and the call to this method returns 4.This value is only an estimated number because the number of threads can change dynamically as this method traverses the internal data structure.This method is used to monitor the state of the system and does not require synchronous control.

(3) int getWaitQueueLength(Condition condition) returns an estimate of the thread waiting for a given condition condition condition condition associated with this lock, such as five threads, each executing the await() method of the same condition object, and the value returned by calling this method is 5.

public class Service {
    private ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();

    public void methodA(){
        try {
            lock.lock();
            System.out.println("A getHoldCount call lock Number of times=>"+lock.getHoldCount());
            Thread.sleep(2000);
            System.out.println("A getQueueLength Number of threads waiting=>"+lock.getQueueLength());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //test getWaitQueueLength Method
    public Integer methodC(){
        try {
            lock.lock();
            return lock.getWaitQueueLength(condition);
        }finally {
            lock.unlock();
        }

    }
}
public class Run{

    public static void main(String[] args) throws InterruptedException {
        Service service=new Service();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
               service.methodA();
            }
        };

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

        Thread.sleep(1000);
        System.out.println("Executed the same Condition Object's await()Threads are:"+service.methodC());
    }
}

(4) boolean hasQueuedThread(Thread thread) queries whether the specified thread is waiting to acquire this lock.

(5) boolean hasQueuedThreads() queries whether a thread is waiting to acquire this lock.

(6) boolean hasWaiters(Condition condition) queries whether a thread is waiting for a condition condition associated with this lock

(7) boolean isFair() judges whether the lock is fair.

(8) boolean isHelpByCurrentThread() queries if the current thread holds this lock.

(9) boolean isLocked() queries whether the lock is maintained by any thread.

 

(10) void lockInterruptibly() Acquires a lock if the current thread has not been interrupted, or an exception if it has been interrupted.

(11) boolean tryLock() acquires the lock only if it was not locked by another thread at the time of the call.

(12) boolean tryLock(long timeout,TimeUnit unit) acquires a lock if it is not held by another thread within a given wait time and the current thread is not interrupted.

public class Service
{
    private ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();

    //test lockInterruptibly
    public void methodA(){
        try {
            lock.lockInterruptibly();
            System.out.println("methodA=>"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if (lock.isHeldByCurrentThread()){//Release if the current thread remains locked on this lock
                lock.unlock();
            }
        }
    }
    //test tryLock
    public void methodB(){
          if (lock.tryLock()){
              System.out.println(Thread.currentThread().getName()+"Acquire locks");
          }else{
              System.out.println(Thread.currentThread().getName()+"No Lock Acquired");
          }
    }
}
public class Run
{
    public static void main(String[] args) throws InterruptedException
    {
        Service service=new Service();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
                service.methodA();
                service.methodB();
            }
        };

        Thread threadA=new Thread(runnable);
        threadA.setName("A");
        threadA.start();

        Thread.sleep(1000);
        Thread threadB=new Thread(runnable);
        threadB.setName("B");
        threadB.start();
        threadB.interrupt();
    }
}

 

(13) lock.awaitUninterruptibly(): This thread will not be interrupted until other threads call the signal() or signalAll() methods.

(14) lock.awaitUntil(Date date): This thread will sleep until:

  • It was interrupted
  • Other threads call the singal() or signalAll() method on this condition
  • The specified date has arrived

3. Use the ReentrantReadWriteLock class

The RenntrantLock class has a completely exclusive effect, that is, only one thread at a time performs tasks following the RenntrantLock.lock() method.This guarantees thread security for instance variables, but it is inefficient because even if there are times when there is no write in the lock, it will not be read until the lock is released.So JDK provides a read-write lock ReentrantReadWriteLock class that can be used to speed up operations.*
ReentrantReadWriteLock has two locks, one is a read-related lock, also known as a shared lock, and the other is a write-related lock, also known as an exclusive lock.That is, multiple read locks are not mutually exclusive, read and write locks are mutually exclusive, and write and write locks are mutually exclusive.When there is no thread Thread for a write operation, multiple Threads performing a read operation can acquire a read lock.Thread, which does write operations, can only write after acquiring a write lock.That is, multiple Threads can read at the same time, but only one Thread is allowed to write at the same time.
 
Reading is not mutually exclusive:
public class Read
{
    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    public void read(){
        try {
            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName()+"Reading"+System.currentTimeMillis());
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }
}
public class Run
{
    public static void main(String[] args)
    {
        Read read=new Read();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
                read.read();
            }
        };
        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++){
            threads[i]=new Thread(runnable);
            threads[i].start();
            //As a result, you can see that all threads are entering at almost the same time lock()Method
            //Later code, read not mutually exclusive, can improve the efficiency of the program, allow
            //Multiple threads executing simultaneously lock()Code behind method
        }
    }
}
Write-write exclusion:
public class Write
{
    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    public void write(){
        try {
            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName()+"Writing"+System.currentTimeMillis());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }
}
public class Run
{
    public static void main(String[] args)
    {
        Write write=new Write();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
                write.write();
            }
        };
        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++){
            threads[i]=new Thread(runnable);
            threads[i].start();
            //As a result, you can see that all threads run every two seconds, write and write are mutually exclusive, and threads run synchronously
        }
    }
}

 

In addition, writing, reading and writing are mutually exclusive, so no example is given.In short, as long as "write" operations occur, they are mutually exclusive!

Keywords: Java JDK

Added by phithe on Sat, 25 May 2019 19:22:03 +0300