Java Lock锁Condition接口和newCondition()方法

培训教学 潘老师 6个月前 (11-10) 150 ℃ (0) 扫码查看

synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,之前我们讲过《Java多线程:线程间通信(等待/通知机制)》,ReentrantLock类也可以借助于Condition接口与newCondition()方法。

synchronized关键字在使用notify/notifyAll()方法进行通知时,被通知的线程是由JVM选择的,使用ReentrantLock类结合Condition实例可以实现“选择性通知”。

synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在该一个实例上。

如果执行notifyAll()方法的话就会通知所有处于等待状态的线程,这样会造成很大的效率问题,而Condition可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。从而可以有选择性的进行线程通知,在调度线程上更加灵活。

Condition接口

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

Condition实现等待/通知机制

当调用 await() 语句后,线程将被阻塞,必须执行完signal()所在的try语句块之后才释放锁,condition.await()后的语句才能被执行。

package ReentrantLockTest;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class Test {
 
    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
 
        MyThread a = new MyThread(service);
        a.start();
 
        Thread.sleep(3000);
 
        service.signal();
    }
 
    static public class MyService {
 
        private Lock lock = new ReentrantLock();
 
        public Condition condition = lock.newCondition();
 
        public void await() {
            lock.lock();
            try {
                System.out.println("准备调用condition.await()方法,将该线程阻塞");
                condition.await();
                System.out.println("已调用condition.await()方法,此时已被 signal() 方法唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
 
        public void signal() {
            lock.lock();
            try {
                System.out.println("准备调用condition.signal()方法");
                condition.signal();
                Thread.sleep(3000);
                System.out.println("已调用condition.signal()方法,去唤醒 await() 方法");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
 
 
    static public class MyThread extends Thread {
        private MyService service;
 
        public MyThread(MyService service) {
            this.service = service;
        }
 
        @Override
        public void run() {
            service.await();
        }
    }
 
}

输出如下:

准备调用condition.await()方法,将该程序阻塞
准备调用condition.signal()方法
已调用condition.signal()方法,去唤醒 await() 方法
已调用condition.await()方法,此时已被 signal() 方法唤醒

多个Condition实例实现等待/通知机制

一个Lock对象中可以创建多个Condition实例,调用某个实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。

package ReentrantLockTest;
 
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class LockTest {
 
    private Lock lock = new ReentrantLock();
 
    private Condition conditionA = lock.newCondition();
 
    private Condition conditionB = lock.newCondition();
 
    public void awaitA() {
        lock.lock();
        try {
            System.out.println("准备调用conditionA.await()方法,将该线程阻塞");
            conditionA.await();
            System.out.println(" awaitA 已被唤醒");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 
    public void awaitB() {
        lock.lock();
        try {
            System.out.println("准备调用conditionB.await()方法,将该线程阻塞");
            conditionB.await();
            System.out.println(" awaitB 已被唤醒");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 
    public void signalA() {
        lock.lock();
        try {
            System.out.println("准备唤醒 conditionA 下的所有线程");
            conditionA.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 
    public void signalB() {
        lock.lock();
        try {
            System.out.println("准备唤醒 conditionB 下的所有线程");
            conditionB.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 
}
package ReentrantLockTest;
 
public class Test {
 
    public static void main(String[] args) throws InterruptedException {
 
        LockTest lockTest = new LockTest();
 
        ThreadA a = new ThreadA(lockTest);
        a.setName("A");
        a.start();
 
        ThreadB b = new ThreadB(lockTest);
        b.setName("B");
        b.start();
 
        Thread.sleep(3000);
 
        lockTest.signalA();
 
    }
 
    static public class ThreadA extends Thread {
 
        private LockTest lockTest;
 
        public ThreadA(LockTest lockTest) {
            this.lockTest = lockTest;
        }
 
        @Override
        public void run() {
            lockTest.awaitA();
        }
    }
 
    static public class ThreadB extends Thread {
 
        private LockTest lockTest;
 
        public ThreadB(LockTest lockTest) {
            this.lockTest = lockTest;
        }
 
        @Override
        public void run() {
            lockTest.awaitB();
        }
    }
}

程序输出:

准备调用conditionA.await()方法,将该线程阻塞
准备调用conditionB.await()方法,将该线程阻塞
准备唤醒 conditionA 下的所有线程
 awaitA 已被唤醒

 总结

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。Condition的强大之处在于,对于一个锁,我们可以为多个线程间建立不同的Condition。

如果采用Object类中的wait(), notify(), notifyAll()实现的话,当写入数据之后需要唤醒读线程时,不可能通过notify()或notifyAll()明确的指定唤醒读线程,而只能通过notifyAll唤醒所有线程,但是notifyAll无法区分唤醒的线程是读线程,还是写线程。所以,通过Condition能够更加精细的控制多线程的休眠与唤醒。

以上就是Java Lock锁Condition接口和newCondition()方法的详解。


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/teach/10949.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】