Java Concurrency and Lock Design Implementation Details (11) - Condition in Java

Regarding the waiting notification mechanism, there are mainly two ways in Java. One is based on the synchronized keyword of the wait/notify method collection, which was described in the previous article "Detailed Implementation of Java Concurrency and Lock Design (10) - Waiting/Notification Mechanism in Java" , and the other is The method is to implement Condition combined with Lock.

The following is a simple comparison of these two methods.

Comparison of Object's monitor method and Condition interface
Contrast
Object Monitor Methods
Condition
Precondition
Acquire a lock on an object before using it

Call Lock.lock to acquire the lock;

Call Lock.newCondition to get the Condition object;

call method

Call directly, such as: object.wait()

Direct call, such as condition.await()

number of waiting queues
One
multiple
The current thread releases the lock and enters the wait state
support
support

The current thread releases the lock and enters the waiting state,

Not responding to interrupts in wait state

not support
support
The current thread releases the lock and enters the timeout waiting state
support
support

The current thread releases the lock and enters the timeout waiting state,

Do not respond to interrupts in timeout wait state

not support
support
Wake up a thread in the waiting queue
support
support
Wake up all threads in the waiting queue
support
support

From the above comparison, the functions of the two methods are basically the same, but there are subtle differences in the scenario of responding to interruptions.

Since the waiting notification mechanism based on Object Monitor Methods has been mentioned above, another implementation will be described in this article, that is, the implementation based on the Condition interface.

Interface and example of Condition :

The Condition interface defines two types of methods of waiting/notifying. When a thread calls these methods, it needs to acquire the lock associated with the Condition object in advance (in the solution based on the wait/notify method, the object lock needs to be acquired).

The Condition object needs to be associated with the Lock object, which is created by calling the newCondition() object of the Lock object, that is to say, the use of the Condition needs to depend on the Lock object.

下面是使用Condition的示范代码:

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

	public void conditionAwait() throws InterruptedException {
		lock.lock();
		try {
			condition.await();
		} finally {
			lock.unlock();
		}
	}

	public void conditionSignal() {
		lock.lock();
		try {
			condition.signal();//condition.signalAll();
		} finally {
			lock.unlock();
		}
	}

下面是关于Condition方法的一些简单说明:

方法名称
描述
await()

当前线程进入等待状态直到被通知(signal)或者中断;

当前线程进入运行状态并从await()方法返回的场景包括:

(1)其他线程调用相同Condition对象的signal/signalAll方法,并且当前线程被唤醒;

(2)其他线程调用interrupt方法中断当前线程;

awaitUninterruptibly()
当前线程进入等待状态直到被通知,在此过程中对中断信号不敏感,不支持中断当前线程
awaitNanos(long)
当前线程进入等待状态,直到被通知、中断或者超时。如果返回值小于等于0,可以认定就是超时了
awaitUntil(Date)
当前线程进入等待状态,直到被通知、中断或者超时。如果没到指定时间被通知,则返回true,否则返回false
signal()
唤醒一个等待在Condition上的线程,被唤醒的线程在方法返回前必须获得与Condition对象关联的锁
signalAll()
唤醒所有等待在Condition上的线程,能够从await()等方法返回的线程必须先获得与Condition对象关联的锁

下面通过一个示例来简单看看Condition是如何实现线程等待/通知机制的。

package com.majing.java.concurrent;

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

public class BoundedQueue {
	private Integer[] items;//定义为数组,在创建对象时就确定容量
	private Lock lock = new ReentrantLock();
	private Condition notEmpty = lock.newCondition();
	private Condition notFull = lock.newCondition();
	
	private int count;
	private int addIndex,removeIndex;
	
	public BoundedQueue(int size){
		items = new Integer[size];
	}
	
	public void add(Integer object) throws InterruptedException{
		lock.lock();
		try{
			while(count==items.length){
				notFull.await();
			}
			items[addIndex] = object;
			if(++addIndex==items.length){
				addIndex = 0;
			}
			count++;
			System.out.println(Thread.currentThread()+" 插入一个元素,数组为:"+Arrays.toString(items));
			notEmpty.signal();
		}finally{
			lock.unlock();
		}
	}
	
	@SuppressWarnings("unchecked")
	public Integer remove() throws InterruptedException{
		lock.lock();
		try{
			while(count==0){
				notEmpty.await();
			}
			Integer temp = items[removeIndex];
			items[removeIndex] = null;
			System.out.println(Thread.currentThread()+" 移除一个元素,数组为:"+Arrays.toString(items));
			if(++removeIndex==items.length){
				removeIndex=0;
			}
			count--;
			notFull.signal();
			return temp;
		}finally{
			lock.unlock();
		}
	}
}

上面是有界队列实现,下面写个简单的测试用例来测试这个有界队列是否可靠。

package com.majing.java.test;

import java.util.Random;

import com.majing.java.concurrent.BoundedQueue;




public class Test {
	
	private static final Random random = new Random(System.currentTimeMillis());
	
	public static void main(String[] args) throws InterruptedException {
		
		BoundedQueue queue = new BoundedQueue(5);
		
		for(int i=1;i<=20;i++){
			Thread thread = new Thread(new Producter(queue),String.valueOf(i));
			thread.start();
		}
		
		for(int i=1;i<=20;i++){
			Thread thread = new Thread(new Consumer(queue),String.valueOf(i));
			thread.start();
		}

	}
	
	static class Producter implements Runnable{
		private BoundedQueue queue;
		public Producter(BoundedQueue queue){
			this.queue = queue;
		}
		public void produce() throws InterruptedException{
			queue.add(new Integer(random.nextInt(100)));
		}
		@Override
		public void run() {
			try {
				produce();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
	static class Consumer implements Runnable{
		private BoundedQueue queue;
		public Consumer(BoundedQueue queue){
			this.queue = queue;
		}
		public Integer remove() throws InterruptedException{
			return queue.remove();
		}
		@Override
		public void run() {
			try {
				remove();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

运行上面的代码,结果如下:

Thread[1,5,main] 插入一个元素,数组为:[92, null, null, null, null]
Thread[2,5,main] 插入一个元素,数组为:[92, 87, null, null, null]
Thread[3,5,main] 插入一个元素,数组为:[92, 87, 19, null, null]
Thread[4,5,main] 插入一个元素,数组为:[92, 87, 19, 88, null]
Thread[5,5,main] 插入一个元素,数组为:[92, 87, 19, 88, 22]
Thread[1,5,main] 移除一个元素,数组为:[null, 87, 19, 88, 22]
Thread[2,5,main] 移除一个元素,数组为:[null, null, 19, 88, 22]
Thread[6,5,main] 插入一个元素,数组为:[23, null, 19, 88, 22]
Thread[7,5,main] 插入一个元素,数组为:[23, 26, 19, 88, 22]
Thread[3,5,main] 移除一个元素,数组为:[23, 26, null, 88, 22]
Thread[4,5,main] 移除一个元素,数组为:[23, 26, null, null, 22]
Thread[9,5,main] 插入一个元素,数组为:[23, 26, 30, null, 22]
Thread[8,5,main] 插入一个元素,数组为:[23, 26, 30, 83, 22]
Thread[5,5,main] 移除一个元素,数组为:[23, 26, 30, 83, null]
Thread[6,5,main] 移除一个元素,数组为:[null, 26, 30, 83, null]
Thread[7,5,main] 移除一个元素,数组为:[null, null, 30, 83, null]
Thread[10,5,main] 插入一个元素,数组为:[null, null, 30, 83, 25]
Thread[11,5,main] 插入一个元素,数组为:[13, null, 30, 83, 25]
Thread[12,5,main] 插入一个元素,数组为:[13, 30, 30, 83, 25]
Thread[9,5,main] 移除一个元素,数组为:[13, 30, null, 83, 25]
Thread[8,5,main] 移除一个元素,数组为:[13, 30, null, null, 25]
Thread[13,5,main] 插入一个元素,数组为:[13, 30, 9, null, 25]
Thread[14,5,main] 插入一个元素,数组为:[13, 30, 9, 4, 25]
Thread[10,5,main] 移除一个元素,数组为:[13, 30, 9, 4, null]
Thread[15,5,main] 插入一个元素,数组为:[13, 30, 9, 4, 85]
Thread[11,5,main] 移除一个元素,数组为:[null, 30, 9, 4, 85]
Thread[13,5,main] 移除一个元素,数组为:[null, null, 9, 4, 85]
Thread[16,5,main] 插入一个元素,数组为:[20, null, 9, 4, 85]
Thread[18,5,main] 插入一个元素,数组为:[20, 71, 9, 4, 85]
Thread[14,5,main] 移除一个元素,数组为:[20, 71, null, 4, 85]
Thread[17,5,main] 插入一个元素,数组为:[20, 71, 50, 4, 85]
Thread[16,5,main] 移除一个元素,数组为:[20, 71, 50, null, 85]
Thread[19,5,main] 插入一个元素,数组为:[20, 71, 50, 3, 85]
Thread[17,5,main] 移除一个元素,数组为:[20, 71, 50, 3, null]
Thread[18,5,main] 移除一个元素,数组为:[null, 71, 50, 3, null]
Thread[19,5,main] 移除一个元素,数组为:[null, null, 50, 3, null]
Thread[20,5,main] 插入一个元素,数组为:[null, null, 50, 3, 8]
Thread[20,5,main] 移除一个元素,数组为:[null, null, null, 3, 8]
Thread[12,5,main] 移除一个元素,数组为:[null, null, null, null, 8]
Thread[15,5,main] 移除一个元素,数组为:[null, null, null, null, null]

在这个例子中,实现了一个有界队列,并且这个有界队列的容量我控制在了5个元素,然后通过20个生产者和20个消费者并发去添加和取出元素,从运行结果来看,队列并没有出现异常,生产者和消费者能够相互协调有序的进行。

关于Condition的内部实现和原理,这里先不做说明了,后面如果有时间会另行补充进该博文,想知道的朋友可以自己去查看下源码.

感谢大家的阅读,如果有对Java编程、中间件、数据库、及各种开源框架感兴趣,欢迎关注我的博客和头条号(源码帝国),博客和头条号后期将定期提供一些相关技术文章供大家一起讨论学习,谢谢。

如果觉得文章对您有帮助,欢迎给我打赏,一毛不嫌少,一百不嫌多,^_^谢谢。




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325915195&siteId=291194637