首页 > 代码库 > 多线程编程-- part 5.2 JUC锁之Condition条件

多线程编程-- part 5.2 JUC锁之Condition条件

 

 

 

 

 

1.Condition介绍

  Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。

 

2.Condition函数列表

// 造成当前线程在接到信号或被中断之前一直处于等待状态。void await()// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。boolean await(long time, TimeUnit unit)// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。long awaitNanos(long nanosTimeout)// 造成当前线程在接到信号之前一直处于等待状态。void awaitUninterruptibly()// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。boolean awaitUntil(Date deadline)// 唤醒一个等待线程。void signal()// 唤醒所有等待线程。void signalAll()

3.Condition示例

(1)通过Object的wait(),notify()来演示线程休眠/唤醒

(2)是通过Condition的await(), signal()来演示线程的休眠/唤醒功能。
(3)是通过Condition的高级功能。

示例1:

public class testHello {    public static void main(String[] args) {        ThreadA ta = new ThreadA("ta");        synchronized(ta) { // 通过synchronized(ta)获取“对象ta的同步锁”            try {                System.out.println(Thread.currentThread().getName()+" start ta");                ta.start();                System.out.println(Thread.currentThread().getName()+" block");                ta.wait();    // 等待                System.out.println(Thread.currentThread().getName()+" continue");            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    static class ThreadA extends Thread{        public ThreadA(String name) {            super(name);        }        public void run() {            synchronized (this) { // 通过synchronized(this)获取“当前对象的同步锁”                System.out.println(Thread.currentThread().getName()+" wakup others");                notify();    // 唤醒“当前对象上的等待线程”            }        }    }}

技术分享

示例2:

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class ConditionTest1 {            private static Lock lock = new ReentrantLock();    private static Condition condition = lock.newCondition();    public static void main(String[] args) {        ThreadA ta = new ThreadA("ta");        lock.lock(); // 获取锁        try {            System.out.println(Thread.currentThread().getName()+" start ta");            ta.start();            System.out.println(Thread.currentThread().getName()+" block");            condition.await();    // 等待            System.out.println(Thread.currentThread().getName()+" continue");        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            lock.unlock();    // 释放锁        }    }    static class ThreadA extends Thread{        public ThreadA(String name) {            super(name);        }        public void run() {            lock.lock();    // 获取锁            try {                System.out.println(Thread.currentThread().getName()+" wakup others");                condition.signal();    // 唤醒“condition所在锁上的其它线程”            } finally {                lock.unlock();    // 释放锁            }        }    }}

技术分享

Condition:

  Condition除了支持上面的功能之外,它更强大的地方在于:能够更加精细的控制多线程的休眠与唤醒。对于同一个锁,我们可以创建多个Condition,在不同的情况下使用不同的Condition。
例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据之后,唤醒"读线程";当从缓冲区读出数据之后,唤醒"写线程";并且当缓冲区满的时候,"写线程"需要等待;当缓冲区为空时,"读线程"需要等待。         如果采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据之后需要唤醒"读线程"时,不可能通过notify()或notifyAll()明确的指定唤醒"读线程",而只能通过notifyAll唤醒所有线程(但是notifyAll无法区分唤醒的线程是读线程,还是写线程)。  但是,通过Condition,就能明确的指定唤醒读线程。

class BoundedBuffer {    final Lock lock = new ReentrantLock();    final Condition notFull  = lock.newCondition();    final Condition notEmpty = lock.newCondition();    final Object[] items = new Object[5];    int putptr, takeptr, count;    public void put(Object x) throws InterruptedException {        lock.lock();    //获取锁        try {            // 如果“缓冲已满”,则等待;直到“缓冲”不是满的,才将x添加到缓冲中。            while (count == items.length)                notFull.await();            // 将x添加到缓冲中            items[putptr] = x;            // 将“put统计数putptr+1”;如果“缓冲已满”,则设putptr为0。            if (++putptr == items.length) putptr = 0;            // 将“缓冲”数量+1            ++count;            // 唤醒take线程,因为take线程通过notEmpty.await()等待            notEmpty.signal();            // 打印写入的数据            System.out.print(Thread.currentThread().getName() + " put  ");            for(int i = 0; i < items.length; i++) {                System.out.print(items[i] + "  ");            }            System.out.println("\n");        } finally {            lock.unlock();    // 释放锁        }    }    public Object take() throws InterruptedException {        lock.lock();    //获取锁        try {            // 如果“缓冲为空”,则等待;直到“缓冲”不为空,才将x从缓冲中取出。            while (count == 0)                notEmpty.await();            // 将x从缓冲中取出            Object x = items[takeptr];            items[takeptr] = null;            // 将“take统计数takeptr+1”;如果“缓冲为空”,则设takeptr为0。            if (++takeptr == items.length) takeptr = 0;            // 将“缓冲”数量-1            --count;            // 唤醒put线程,因为put线程通过notFull.await()等待            notFull.signal();            // 打印取出的数据            System.out.print(Thread.currentThread().getName() + " take ");            for(int i = 0; i < items.length; i++) {                System.out.print(items[i] + "  ");            }            System.out.println("\n");            return x;        } finally {            lock.unlock();    // 释放锁        }    }}public class testHello {    private static BoundedBuffer bb = new BoundedBuffer();    static class PutThread extends Thread {        private int num;        public PutThread(String name, int num) {            super(name);            this.num = num;        }        public void run() {            try {                Thread.sleep(1);    // 线程休眠1ms                bb.put(num);        // 向BoundedBuffer中写入数据            } catch (InterruptedException e) {            }        }    }    static class TakeThread extends Thread {        public TakeThread(String name) {            super(name);        }        public void run() {            try {                Thread.sleep(10);                    // 线程休眠1ms                Integer num = (Integer)bb.take();    // 从BoundedBuffer中取出数据            } catch (InterruptedException e) {            }        }    }    public static void main(String[] args) {        // 启动10个“写线程”,向BoundedBuffer中不断的写数据(写入0-9);        // 启动10个“读线程”,从BoundedBuffer中不断的读数据。        for (int i=0; i<10; i++) {            new PutThread("p"+i, i).start();            new TakeThread("t"+i).start();        }    }}

技术分享

结果说明:

(1) BoundedBuffer 是容量为5的缓冲,缓冲中存储的是Object对象,支持多线程的读/写缓冲。多个线程操作“一个BoundedBuffer对象”时,它们通过互斥锁lock对缓冲区items进行互斥访问;而且同一个BoundedBuffer对象下的全部线程共用“notFull”和“notEmpty”这两个Condition。
       notFull用于控制写缓冲,notEmpty用于控制读缓冲。当缓冲已满的时候,调用put的线程会执行notFull.await()进行等待;当缓冲区不是满的状态时,就将对象添加到缓冲区并将缓冲区的容量count+1,最后,调用notEmpty.signal()缓冲notEmpty上的等待线程(调用notEmpty.await的线程)。 简言之,notFull控制“缓冲区的写入”,当往缓冲区写入数据之后会唤醒notEmpty上的等待线程。
       同理,notEmpty控制“缓冲区的读取”,当读取了缓冲区数据之后会唤醒notFull上的等待线程。
(2) 在ConditionTest2的main函数中,启动10个“写线程”,向BoundedBuffer中不断的写数据;同时,也启动10个“读线程”,从BoundedBuffer中不断的读数据。

多线程编程-- part 5.2 JUC锁之Condition条件