在我们使用同步块的时候synchronized关键字+wait/notify是我们最常使用的一种代码同步加锁等待唤醒方式,但是在使用过程中我们发现synchronized关键字+wait/notify只能一次性的针对某个条件进行同步加锁,不能跨条件加锁,意思就是说如果有2个地方需要加锁,并且加锁和解锁是相互制约的,那么使用synchronized就不能进行同步加锁,没有办法使用wait/notify进行唤醒操作,因为wait/notify是只能对同一个条件进行唤醒,这个时候我们可以使用到Condition来进行控制,比如我们有一个队列,需要进行队列为空或者满时的等待,如果使用wait/notify的方式是没有办法控制的,因为我们这里提供了take和put方法,并且我们这里还需要精准的控制是等待take还是put的执行,如下代码

public class ConditionTest {
    Lock lock = new ReentrantLock();

    Condition fullCondition = lock.newCondition();
    Condition emptyCondition = lock.newCondition();

    Object[] objects = new Object[100];

    int take,put,count;

    public void put(Object obj) throws InterruptedException {
        lock.lock();
        try {
            while (count == objects.length){
                fullCondition.await();
            }

            objects[put] = obj;
            System.out.println("put:"+obj);

            if (++put == objects.length){
                put = 0;
            }

            count++;
            emptyCondition.signal();
        }finally {
            lock.unlock();
        }
    }

    public void take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0){
                emptyCondition.await();
            }

            Object obj = objects[take];
            System.out.println("take:"+obj);

            if (++take == objects.length){
                take = 0;
            }

            count--;
            fullCondition.signal();
        }finally {
            lock.unlock();
        }
    }

    public static void main(String [] args){
        ConditionTest conditionTest = new ConditionTest();

        new Thread(() ->{
            int i = 0;
            for (;;){
                try {
                    conditionTest.put(i);
                    i++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (;;){
                try {
                    Thread.sleep(1000);
                    conditionTest.take();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

       在上面我们精准的控制队列满时那么就让fullCondition条件进行加锁等待,对emptyCondition进行唤醒操作,队列空时就让emptyCondition条件进行加锁等待,对fullCondition进行唤醒操作,因此这个相比wait/notify来说控制的更好了。

      我们总结下Condition和wait/notify的比较:

            1.Condition可以精准的对多个不同条件进行控制,wait/notify只能和synchronized关键字一起使用,并且只能唤醒一个或者全部的等待队列;

            2.Condition需要使用Lock进行控制,使用的时候要注意lock()后及时的unlock(),Condition有类似于await的机制,因此不会产生加锁方式而产生的死锁出现,同时底层实现的是park/unpark的机制,因此也不会产生先唤醒再挂起的死锁,一句话就是不会产生死锁,但是wait/notify会产生先唤醒再挂起的死锁。

     在这里同时总结下synchronized关键字和Lock的比较:

     synchronized:

             1.使用简单,语义清晰,可以在任何地方使用

锁粗化、锁消除、偏向锁、轻量级锁)

             3.由JVM进行锁释放,不用手动操作,减少了产生死锁的可能性

如:公平锁、中断锁、超时锁、读写锁、共享锁等

      Lock:

            1.可以实现所有不能再synchronized关键字中的实现的锁的高级功能

            2.可以通过实现Lock接口来实现自定义的一些方法

            3.使用时一定要记住unlock锁,如果不释放,容易造成死锁。

      这里还有一个读写锁ReadWriteLock简单说明一下,它的实现类ReentrantReadWriteLock,是一个可重入的读写锁,它里面提供了ReadLock和WriteLock,分别用于读时加锁和写时加锁,针对同一个线程先有写锁时,可以获取到读锁,但是先有读锁时不能获取到写锁,必须写锁完成后才能读锁,针对不同线程时,如果已经有读写锁,那么其他线程必须等待当前线程释放读写锁后才能去获取读写锁。