java并发编程:自定义同步工具

条件谓词

在进行某些操作之前,需要进行一些先置条件判断。例如阻塞队列BlockingQueue在进行put操作之前,要先判断队列是否已满,如果已满,就会阻塞,直到队列空出位置出来,才能释放出来,继续执行操作。其中,“队列是否已满”就是条件谓词。

条件队列

在操作不满足条件谓词时,就会被阻塞,该线程就会被塞入对应锁的等待队列中,这个队列即为条件队列。

使用notify的危害

队列中如果有多种条件谓词,可能出现当条件谓词A满足条件后,打算唤醒线程A,但是由于notify的随机唤醒机制,却唤醒了条件谓词为B的线程B,导致线程A一直等不到唤醒信号,线程B被唤醒了,但是它的条件谓词却不为真。

使用notify的优点

notify比notifyAll高效。notifyAll是把所有等待的线程全部唤醒,这些线程会发生竞争,等到其中一个线程获取到锁之后,其它大部分线程就又会回到休眠状态。这过程中,将会出现大量的线程竞争和线程上下文切换。

使用notify的条件

  1. 条件队列中,只有一种条件谓词
  2. 条件队列为单进单出队列

信号丢失问题

  1. 使用notify可能引起信号丢失
  2. 条件谓词为真,线程被唤醒之后,条件谓词却又被其它线程修改为假了

信号丢失问题解决办法

  1. 尽量不使用notify,要使用的话,一定要满足上述使用notify的所有条件
  2. 在线程被唤醒之后,对条件谓词进行再次判断,如果不为真,则继续等待

使用object自带的方法创建同步工具

我们这里用object自带的方法来实现阻塞队列。

public class BlockingQueueUseObject {
    private Object[] array = new Object[10];
    private int count = 0;

    public synchronized void put(Object obj) throws InterruptedException {
        while (count == array.length) {
            this.wait();
        }
        array[count] = obj;
        count++;
        if (count == 1) {
            this.notifyAll();
        }
    }

    public synchronized Object take() throws InterruptedException {
        while (count == 0) {
            this.wait();
        }
        count--;
        if (count == array.length - 1) {
            this.notifyAll();
        }
        return array[count];
    }
}

使用显示的Condition对象

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

public class BlockingQueueUseObject {
    private ReentrantLock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();
    private Object[] array = new Object[10];
    private int count = 0;

    public synchronized void put(Object obj) throws InterruptedException {
        lock.lock();
        try {
            while (count == array.length) {
                notFull.await();
            }
            array[count] = obj;
            count++;
            if (count == 1) {
                notEmpty.signal();
            }
        } finally {
            lock.unlock();
        }
    }

    public synchronized Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await();
            }
            count--;
            if (count == array.length - 1) {
                notFull.signal();
            }
            return array[count];
        } finally {
            lock.unlock();
        }
    }
}

使用Condition的优点

每个条件队列都与锁绑定,JVM内置锁只能绑定一个条件队列,如果有多个条件谓词的时候,只能使用notifyAll,比较低效。而显示锁ReentrantLock可以创建多个Condition,每个Condition自带一个条件队列,能够使用比较高效的signal。

你可能感兴趣的:(java并发编程)