This is the second day of my participation in Gwen Challenge

We know that the AQS class is at the heart of all concurrent programming locks, so in practice we need to know what methods the Lock interface defines for us.

Lock is used for access to shared resources in concurrent programming. Typically, a Lock object provides an exclusive way to access the object, known as an exclusive Lock — only one thread can acquire the Lock and access the resource at a time. However, some locks that provide concurrent access to shared resources, such as ReadWriteLock, are called shared locks.

Synchronized vs. Lock:

Methods or code segments that use synchronized synchronization have implicit lock listeners (locks).

public synchronized void doGet(a){}Copy the code

When different locks are requested, they must be released in the same scope in the appropriate order.

Lock l = ... ; l.lock();try {
  // access the resource protected by this lock
 } finally {
  l.unlock();
}}
Copy the code

1. Lock source analysis

Let’s first look at the definition of Lock.

public interface Lock {

    /** * Acquires the lock. * If the current thread cannot acquire the lock, the current thread goes to sleep and becomes unavailable until the current thread Acquires the lock. * If the lock is not held by another thread, the lock is acquired and returned immediately, setting the lock hold count to 1. * /
    void lock(a);

    /** * 1) If the current thread is not interrupted, the lock is acquired. * 2) If the lock is not held by another thread, the lock is acquired and returned immediately, setting the lock hold count to 1. * 3) If the current thread already holds the lock, the hold count is increased by one, and the method returns immediately. * 4) If the lock is held by another thread, the current thread is disabled for thread scheduling purposes, and the thread will remain dormant until * occurs in one of two ways: * 1) The lock is acquired by the current thread; Or * 2) some other thread interrupts the current thread. * 5) If the current thread acquires the lock, the lock hold count is set to 1. * If the current thread: * 1) the thread's interrupt state has been set when entering this method; * 2) Is interrupted while waiting to acquire the lock. * throws InterruptedException and clears the interrupted status of the current thread. * 6) In this implementation, because this method is an explicit breakpoint, the response interrupt is prioritized over the normal or * reentrant acquisition of the response lock. * /
    void lockInterruptibly(a) throws InterruptedException;

    /** * Attempts to acquire the lock only if it is not held by another thread when called. * /
    boolean tryLock(a);

    / * * * * /
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    /** * only the thread holding the lock can release the lock */
    void unlock(a);

    /** * returns an instance of Condition bound to the Lock object. The current thread must hold the corresponding Lock object before the Condition object can be acquired. * /
    Condition newCondition(a);
}
Copy the code

In the source code above, we can see that Lock provides a Lock () method to acquire the Lock and unlock() method to release the Lock. Take the following example:

public class ConcrateDemo {
	private Lock lock = new ReentrantLock();
	
	public static void main(String []args){
		MyRunnable runnable = new ConcrateDemo().new MyRunnable(a);
		Thread thread1 = new Thread(runnable);
		Thread thread2 = new Thread(runnable);
		thread1.start();
		thread2.start();
	}
	class MyRunnable implements Runnable{

		public void run(a) {
			lock.lock();
			for(int i=0; i<5; i++){ System.out.println("currentThread:" + Thread.currentThread().getName()
						+ "==Cnt:"+ i); } lock.unlock(); }}}Copy the code

2. Condition source analysis

When we introduced the Lock class above, there was a newCondition method:

/** * returns an instance of Condition bound to the Lock object. The current thread must hold the corresponding Lock object before the Condition object can be acquired. * /
Condition newCondition(a);
Copy the code

From this you can guess that a Condition object should be bound to a Lock. Condition is a class that Java provides to implement wait/notification.

Object provides wait, waitAll, notify, and notifyAll to synchronize, wait, and wake up threads. The Condition class provides more functionality than wait/notify. The Condition object is created by the lock object. A lock can create multiple Condition objects, that is, multiple object listeners, which can be specified to wake up a particular thread. Notify is a random wake up thread.

public interface Condition {

    /** * causes the current thread to wait until it receives a signal or is interrupted. *@throws InterruptedException if the current thread is interrupted
     *         (and interruption of thread suspension is supported)
     */
    void await(a) throws InterruptedException;

    /** * causes the current thread to wait until it receives a signal or is interrupted. * /
    void awaitUninterruptibly(a);

    /** * Makes the current thread wait until it receives a signal, is interrupted, or reaches the specified wait time. *@param nanosTimeout the maximum time to wait, in nanoseconds
     * @return an estimate of the {@code nanosTimeout} value minus
     *         the time spent waiting upon return from this method.
     *         A positive value may be used as the argument to a
     *         subsequent call to this method to finish waiting out
     *         the desired time.  A value less than or equal to zero
     *         indicates that no time remains.
     * @throws InterruptedException if the current thread is interrupted
     *         (and interruption of thread suspension is supported)
     */
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    /** * Makes the current thread wait until it receives a signal, is interrupted, or reaches the specified wait time. *@param time the maximum time to wait
     * @param unit the time unit of the {@code time} argument
     * @return {@code false} if the waiting time detectably elapsed
     *         before return from the method, else {@code true}
     * @throws InterruptedException if the current thread is interrupted
     *         (and interruption of thread suspension is supported)
     */
    boolean await(long time, TimeUnit unit) throws InterruptedException;

    /** * Wake up a waiting thread. * /
    void signal(a);

    /** * Wake up all waiting threads. * /
    void signalAll(a);
}
Copy the code

As you can see from the source code comments above, Condition provides the following methods:

  • void await(): causes the current thread to wait until it receives a signal or is interrupted.
  • 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, is interrupted, or reaches the specified wait time.
  • void awaitUninterruptibly(): causes the current thread to wait until it receives a signal.
  • boolean awaitUntil(Date deadline): Causes the current thread to wait until it receives a signal, is interrupted, or reaches a specified deadline.
  • void signal(): Wakes up a waiting thread.
  • void signalAll(): Wakes up all waiting threads.

In real development, the thread waits with the await method and the signal wakes up. Note that Condition instances are just plain objects that can themselves be used as targets in synchronized statements and can invoke their own WAIT and Notification monitor methods.

3. Producer-consumer examples

/** * Realization of the example of production consumers * requirements: * There are two forces: production and consumption * When the warehouse is full, consumers should notify the consumer to consume and stop production * When the warehouse is empty, consumers should notify the producer to produce and stop consumption * otherwise, normal production and consumption. * * In the producer-consumer model, the following points should be guaranteed: * 1 only one producer can produce at the same time * 2 Only one consumer can consume at the same time * 3 Consumers cannot continue consuming when the shared space is empty * 4 Producers cannot continue producing when the shared space is full *@author mr_dsw
 */
public class ConcrateDemo {
	public static void main(String []args){
		Resource resource = new Resource();
		ProduceThread produceThread = new ProduceThread(resource);
		ConsumeThread consumeThread = new ConsumeThread(resource);
		// Four producers
		new Thread(produceThread).start();
		new Thread(produceThread).start();
		new Thread(produceThread).start();
		new Thread(produceThread).start();
		// Four consumers
		new Thread(consumeThread).start();
		new Thread(consumeThread).start();
		new Thread(consumeThread).start();
		newThread(consumeThread).start(); }}class Resource{
	private final int MAX_SIZE = 10;
	private LinkedList<Object> list = new LinkedList<Object>();
	private Lock lock = new ReentrantLock();
	private Condition fullCondition = lock.newCondition();
	private Condition emptyCondition = lock.newCondition();
	
	/** * produces goods, there are multiple producers */
	public void produce(a){
		// If production is full, wake up the consumer
		lock.lock();
		while(list.size() == MAX_SIZE){
			System.out.println("Production is full, temporarily unable to produce:" + list.size());
			emptyCondition.signal();
			try {
				fullCondition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		list.add(new Object());
		System.out.println(Thread.currentThread().getName() + "To produce new products, there are:" + list.size());
		lock.unlock();
	}
	
	/** * consumers, there are multiple consumers */
	public void consume(a){
		lock.lock();
		while(list.size() == 0){
			System.out.println("We are out of goods. We need to notify production.");
			fullCondition.signal();
			try {
				emptyCondition.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName() + "Consumer products, collectively:"+ list.size()); list.remove(); lock.unlock(); }}class ProduceThread implements Runnable{
	private Resource resource;
	
	public ProduceThread(Resource resource){
		this.resource = resource;
	}
	
	public void run(a) {
		for(;;) resource.produce(); }}class ConsumeThread implements Runnable{
	private Resource resource;
	
	public ConsumeThread(Resource resource){
		this.resource = resource;
	}
	
	public void run(a) {
		for(;;) resource.consume(); }}Copy the code

4. To summarize

ConditionObject is the key to Condition. ConditionObject is the key to Condition. ConditionObject is a FIFO queue. Each node in the queue is a reference to a thread waiting on the Condition. After calling the await() method of Condition, the thread releases the lock and constructs the corresponding node into the queue to wait. The Node definition uses the Node definition of AQS.

This is the basic learning of Lock and Condition in Java concurrent programming.