写在前面:有人问我,为什么这几篇没有前面几篇详细,我这里解释一下,不是我偷懒,因为这几篇的内容,是给我们另一种解决多线程的方式,没有所谓最好的方法,更多的是要考自己的领悟,是动手敲出来的,我希望观看此文章的读者能够自己动手,然后自己总结,可能收获会更大
内容目录:
Condition接口
正文:
前面一篇 我们讨论Lock接口的时候提到了 可以对 线程挂起状态进行操作,操作线程挂起状态,Condition接口就是对线程挂起状态操作的类
我们先简单的了解一下,什么是线程挂起状态:简单点说,就是让线程不运行的一种状态,叫挂起
前面我们在将线程基础的时候,我们提了让线程挂起的几个函数,我们再来一起简单的回顾一下:
public void sleep()/sleep(long millis , int nano); 睡眠
public void join()/join(long millis , int nano); 等待结束
public void yield(); 暂停
public void notify(); 休眠
具体的使用我这里就不过多的阐述了,在作者的前面几篇博客中我做了简单的讲解,在后面的博文中我们还会继续讲解
上面的这些函数都可以操作线程 让它处于挂起状态,使用Condition也能达到相同的效果,我们先来了解一下Condition这个类的一些常用的函数
public void await(); --
public boolean await(long time , TimeUnit unit);
public long awaitNanos(long nanos);
public void awaitUninterruptibly();
public boolean awaitUntil(Date deadline);
public void signal();
public void signalAll();
实例:
package test3;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class T {
public static void main(String[] args) throws InterruptedException{
ItemQueue itemQueue = new ItemQueue(10);
//Create a producer and a consumer.
Thread producer = new Producer(itemQueue);
Thread consumer = new Consumer(itemQueue);
//Start both threads.
producer.start();
consumer.start();
//Wait for both threads to terminate.
producer.join();
consumer.join();
}
static class ItemQueue {
private Object[] items = null;
private int current = 0;
private int placeIndex = 0;
private int removeIndex = 0;
private final Lock lock;
private final Condition isEmpty;
private final Condition isFull;
public ItemQueue(int capacity) {
this.items = new Object[capacity];
lock = new ReentrantLock();
isEmpty = lock.newCondition();
isFull = lock.newCondition();
}
public void add(Object item) throws InterruptedException {
lock.lock();
while(current >= items.length)
isFull.await();
items[placeIndex] = item;
placeIndex = (placeIndex + 1) % items.length;
++current;
//Notify the consumer that there is data available.
isEmpty.signal();
lock.unlock();
}
public Object remove() throws InterruptedException {
Object item = null;
lock.lock();
while(current <= 0){
isEmpty.await();
}
item = items[removeIndex];
removeIndex = (removeIndex + 1) % items.length;
--current;
//Notify the producer that there is space available.
isFull.signal();
lock.unlock();
return item;
}
public boolean isEmpty(){
return (items.length == 0);
}
}
static class Producer extends Thread {
private final ItemQueue queue;
public Producer(ItemQueue queue) {
this.queue = queue;
}
@Override
public void run() {
String[] numbers = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"};
try {
for(String number: numbers){
queue.add(number);
System.out.println("[Producer]: " + number);
}
queue.add(null);
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
static class Consumer extends Thread {
private final ItemQueue queue;
public Consumer(ItemQueue queue) {
this.queue = queue;
}
@Override
public void run() {
try {
do {
Object number = queue.remove();
System.out.println("[Consumer]: " + number);
if(number == null){
return;
}
} while(!queue.isEmpty());
}
catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
现在我们在思考一下Object中wait函数的作用:
Object wait(); 让线程休眠,直到调用notify()或者notifyAll()才有可能执行,那么请各位思考下面的这个场景:
线程1 : synchronized(obj){
obj.wait();
}
线程2: synchronized(obj){
obj.wait();
}
线程3:synchronized(obj){
obj.notify();
}
假设现在我们的执行想要执行的顺序是1-->3-->2(先wait然后notify),那么试想当3 notify运行之后2一定会执行吗?答案是不一定的,这个就要看那个线程的优先级高了,如果是一样的优先级那么他们竞争CPU是一样的,也就是1和2是都有可能执行的,这就是普通方法挂起线程的缺点,为了解决这种问题,我们就可以使用Condition中的挂起状态了
例如:
ReentrantLock r1 = new ReentrantLock(true); 表示各个线程之间可以公平的竞争锁
Condition c1 = r1.newCondition();
Condition c2 = r1.newCondition();
线程1:
r1.lock;
c1.await(); // 第一把锁
r1.unlock();
线程2:
r2.lock();
c2.await(); //第二把锁
r2.unlock();
线程3:
r3.lock();
c1.signal(); //唤醒第一把锁,继续运行线程1await()之后的脚本
r3.unlock();
由此可以看出得出结论:
Object 因为锁是同一个对象,所以在处理线程挂起状态的时候,不是特别灵活
Condition可以添加多个锁操作对象,所以更加灵活