juc并发编程03——Lock与Condition接口

简介: Lock与Condition接口

Lock与Condition接口

前面两篇文章回顾了传统的synchronized关键字、JMM内存模型、volitile关键字,这篇文章开始我们正式介绍juc包。

在jdk5之后,juc提供了Lock,与synchronized相似,都可以实现锁功能,但是需要手动获取锁和释放锁。不过它与synchronized又不太一样,synchronized关键字相当于是使用的其它类的monitor关联,在mark word中记录锁的信息。而Lock则可以认为是一个锁对象

看看Lock源码的方法

public interface Lock {
    //获取锁
    void lock();
   // 获取锁,等待过程中响应中断
    void lockInterruptibly() throws InterruptedException;
   // 尝试获取锁,不会阻塞,成功则返回true,否则返回false
    boolean tryLock();
    // 尝试获取锁,如果获取不到则等待限定时间,超时未获取锁则返回false,可以响应中断
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //释放锁
    void unlock();
    // 可以理解为wait、notify的替代方案
    Condition newCondition();
}

简单应用下

public class Demo8 {
    private static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Runnable action = () -> {
            for (int j = 0; j < 100000; j++) {
                lock.lock();
                i ++;
                lock.unlock();
            }
        };
        new Thread(action).start();
        new Thread(action).start();
        Thread.sleep(1000);
        System.out.println(i);
    }
}

可以看到,使用Lock,是真正的在操作一个锁对象。

使用synchronized可以进行waitnotify

  synchronized (Demo8.class) {
            Demo8.class.wait();
 }

使用Lock配合condition接口可以实现类似功能。而且,使用synchronized关键字只能使用其加锁对象的wait和notify方法,只能有一个等待队列,就是加锁对象(如Demo8.class)的等待队列。而使用Lock和Condition则可以有多个等待队列。


来一起阅读下Condition类的源码

public interface Condition {
   // 对应wait()方法,需要signal()或者signalAll()唤醒,可以响应中断
    void await() throws InterruptedException;
   // 等待,不响应中断
    void awaitUninterruptibly();
   // 等待指定时间(纳秒),如果未超时被唤醒则返回剩余时间,超时返回false,可以响应中断
    long awaitNanos(long nanosTimeout) throws InterruptedException;
  // 等待指定时间,如果未超时被唤醒则返回true,超时返回0或者负数,可以响应中断
    boolean await(long time, TimeUnit unit) throws InterruptedException;
   // 指定明确的时间点,在该时间点前被唤醒则返回true,否则false,可以响应中断
    boolean awaitUntil(Date deadline) throws InterruptedException;
   // 唤醒任一线程,该线程进入就绪状态
    void signal();
    // 唤醒所有线程
    void signalAll();
}

下面实战使用下

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        new Thread(() -> {
            lock.lock();
            System.out.println("await....");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("await finished");
            lock.unlock();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
            lock.lock();
            System.out.println("signal...");
            condition.signal();
            System.out.println("thread 2 finished");
            lock.unlock();
        }).start();
    }
}

输出的结果如下

await....
signal...
thread 2 finished
await finished

值的关注的一点是,不管使用await还是signal都要在获取锁的前提下。这与wait与notify实际上是一致的。不过,在使用了await以后,锁就被释放了,否则主线程也不可能继续执行。


和sychronized关键字不同的是,同一把锁可以创建多个Condition,每个Condition对象都拥有独立的等待队列。看如下代码

public class Demo9 {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Condition condition1 = lock.newCondition();
        new Thread(() -> {
            lock.lock();
            System.out.println("await....");
            try {
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("await finished");
            lock.unlock();
        }).start();
        Thread.sleep(100);
        new Thread(() -> {
            lock.lock();
            System.out.println("signal...");
            condition1.signal();
            System.out.println("thread 2 finished");
            lock.unlock();
        }).start();
    }
}

其输出结果如下。

await....
signal...
thread 2 finished

显然,condition1signal没能唤醒conditionawait的线程。

当然,也并不是用了await就需要signal,因为可以限定时长

public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        lock.lock();
        try {
            System.out.println(condition.await(3, TimeUnit.SECONDS));
        } finally {
            lock.unlock();
        }
    }
}

打印结果为false,因为超过3s也没有被唤醒

相关文章
|
4月前
|
Java
在Java多线程领域,精通Lock接口是成为高手的关键。
在Java多线程领域,精通Lock接口是成为高手的关键。相较于传统的`synchronized`,Lock接口自Java 5.0起提供了更灵活的线程同步机制,包括可中断等待、超时等待及公平锁选择等高级功能。本文通过实战演练介绍Lock接口的核心实现——ReentrantLock,并演示如何使用Condition进行精确线程控制,帮助你掌握这一武林秘籍,成为Java多线程领域的盟主。示例代码展示了ReentrantLock的基本用法及Condition在生产者-消费者模式中的应用,助你提升程序效率和稳定性。
42 2
|
6月前
|
监控 安全 Java
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
Java中的锁(Lock、重入锁、读写锁、队列同步器、Condition)
32 0
|
7月前
|
存储 安全 算法
掌握Java并发编程:Lock、Condition与并发集合
掌握Java并发编程:Lock、Condition与并发集合
53 0
|
安全 Java
JUC第八讲:Condition源码分析
JUC第八讲:Condition源码分析
|
7月前
|
Java
高并发编程之什么是 Lock 接口
高并发编程之什么是 Lock 接口
72 1
|
Java
Java并发编程:使用Lock接口实现线程同步
Java是一种面向对象的编程语言,具有广泛应用于开发各种类型应用程序的能力。并发编程是Java中一个重要的主题,它允许多个任务同时执行,从而提高程序的性能和响应速度。
181 1
JUC并发编程:Condition的简单理解与使用
JUC并发编程:Condition的简单理解与使用
90 0
|
安全 Java
一天一个 JUC 工具类 Lock 和 Condition
当谈到Java多线程编程时,我们不可避免地需要处理并发问题。为此Java提供了一个强大的工具包——java.util.concurrent(JUC)
|
安全 Java
《JUC并发编程 - 基础篇》JUC概述 | Lock接口 | 线程间通信 | 多线程锁 | 集合线程安全(二)
《JUC并发编程 - 基础篇》JUC概述 | Lock接口 | 线程间通信 | 多线程锁 | 集合线程安全
《JUC并发编程 - 基础篇》JUC概述 | Lock接口 | 线程间通信 | 多线程锁 | 集合线程安全(二)