JUC并发编程之等待唤醒机制

简介: 在JUC(Java Util Concurrent)并发编程中,线程等待唤醒机制是实现线程之间协作和同步的重要手段。这种机制允许一个线程挂起等待某个条件满足后被唤醒,以及另一个线程在满足某个条件后唤醒等待的线程。在Java中,有多种实现线程等待唤醒机制的方式,包括使用Object的wait()和notify()方法、Condition接口以及LockSupport类。

1. 线程等待唤醒机制


在JUC(Java Util Concurrent)并发编程中,线程等待唤醒机制是实现线程之间协作和同步的重要手段。这种机制允许一个线程挂起等待某个条件满足后被唤醒,以及另一个线程在满足某个条件后唤醒等待的线程。在Java中,有多种实现线程等待唤醒机制的方式,包括使用Object的wait()和notify()方法、Condition接口以及LockSupport类。


1.1 使用Object的wait()和notify()方法


Object类是Java中所有类的父类,在多线程编程中,我们可以利用Object的wait()和notify()方法实现线程等待唤醒机制。这种方式需要在同步代码块中进行操作,因为wait()和notify()方法必须在同步块中调用。

public class WaitNotifyExample {
    public static void main(String[] args) {
        final Object lock = new Object();
        Thread waitingThread = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Waiting thread: Started waiting...");
                try {
                    lock.wait(); // 线程等待,释放锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Waiting thread: Resumed!");
            }
        });
        Thread notifyingThread = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Notifying thread: Started notifying...");
                lock.notify(); // 唤醒等待线程
                System.out.println("Notifying thread: Finished notifying");
            }
        });
        waitingThread.start();
        try {
            Thread.sleep(1000); // 确保等待线程先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        notifyingThread.start();
    }
}


在上面的示例中,我们创建了两个线程:waitingThread和notifyingThread。waitingThread首先获取锁并调用lock.wait(),进入等待状态,并释放了锁。接着,notifyingThread获取锁,调用lock.notify()唤醒了等待线程。注意,wait()和notify()方法都必须在synchronized块内部使用,否则会抛出IllegalMonitorStateException。


1.2 使用Condition接口


Condition接口是Java并发包(java.util.concurrent.locks)中的一部分,它提供了类似于Object的wait()和notify()方法的功能,但更加灵活。可以在一个Lock对象上通过Condition来实现线程的等待和唤醒。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
    public static void main(String[] args) {
        final Lock lock = new ReentrantLock();
        final Condition condition = lock.newCondition();
        Thread waitingThread = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Waiting thread: Started waiting...");
                condition.await(); // 线程等待
                System.out.println("Waiting thread: Resumed!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });
        Thread notifyingThread = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("Notifying thread: Started notifying...");
                condition.signal(); // 唤醒等待线程
                System.out.println("Notifying thread: Finished notifying");
            } finally {
                lock.unlock();
            }
        });
        waitingThread.start();
        try {
            Thread.sleep(1000); // 确保等待线程先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        notifyingThread.start();
    }
}


在上述示例中,我们使用了ReentrantLock作为锁,通过调用lock.newCondition()创建了一个与该锁关联的Condition实例。然后,等待线程在等待前调用condition.await()进行等待,而唤醒线程通过condition.signal()来唤醒等待的线程。


1.3 使用LockSupport类


LockSupport类是Java并发包(java.util.concurrent.locks)中的工具类,它提供了一种基于线程的阻塞和唤醒机制,不需要依赖于特定的对象实例。


LockSupport的实现依赖于每个线程持有的一个许可(permit)。线程调用park()方法时,如果它持有许可,则立即返回;否则,将会阻塞(挂起)。线程调用unpark(Thread thread)方法时,如果线程处于挂起状态且持有许可,则会使其恢复运行。如果线程尚未调用park()方法或之前已经调用过unpark()方法,并且还持有许可,则后续的park()调用将不会阻塞。而Object类实现线程等待唤醒机制中先notify再wait就会一直处于阻塞状态,Condition接口同理。


LockSupport使用了类似信号量的机制,只有一个许可可供线程使用。如果一个线程调用了unpark(Thread thread)方法,而目标线程还没有调用park()方法,那么目标线程在之后调用park()方法时将立即返回,而不会阻塞。


在内部实现上,LockSupport使用了一些底层的Java本地接口(JNI)来实现线程的挂起和恢复。它通过修改线程的内部状态来达到挂起和恢复线程的目的。


import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
    public static void main(String[] args) {
        Thread waitingThread = new Thread(() -> {
            System.out.println("Waiting thread: Started waiting...");
            LockSupport.park(); // 线程挂起
            System.out.println("Waiting thread: Resumed!");
        });
        Thread notifyingThread = new Thread(() -> {
            System.out.println("Notifying thread: Started notifying...");
            LockSupport.unpark(waitingThread); // 唤醒线程
            System.out.println("Notifying thread: Finished notifying");
        });
        waitingThread.start();
        try {
            Thread.sleep(1000); // 确保等待线程先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        notifyingThread.start();
    }
}


相关文章
|
消息中间件 运维 监控
深入解析Kafka中Replica的妙用
深入解析Kafka中Replica的妙用
797 0
|
Kubernetes 关系型数据库 MySQL
Helm入门(一篇就够了)
Helm入门(一篇就够了)
677 0
|
安全 测试技术
网站CSRF跨站漏洞修复方案
CSRF通俗来讲就是跨站伪造请求攻击,英文Cross-Site Request Forgery,在近几年的网站安全威胁排列中排前三,跨站攻击利用的是网站的用户在登陆的状态下,在用户不知不觉的情况下执行恶意代码以及执行网站的权限操作,CSRF窃取不了用户的数据,只能执行用户能操作的一些数据。比如在用户不知道的情况下, 把账户里的金额,以及银行卡号,体现功能,都转移到其他人账户里去。如果被攻击者是一个管理员的权限,那么就会对网站安全构成严重的危害。
1608 0
网站CSRF跨站漏洞修复方案
|
数据库
港澳台人员社会保障号校验
港澳台人员社会保障号校验
1786 0
|
消息中间件 算法 Kafka
从零开始掌握Kafka Rebalance和分区分配
**Kafka Rebalance详解:**当消费者组成员、订阅主题或分区变化时,集群需重新分配任务。涉及关键点:成员增减、主题数量及分区数变更。Rebalance包括Leader选举、RangeAssignor算法的分区分配,以及创建、删除、修改和查询Topic的基本操作。了解这些有助于优化Kafka集群管理。关注“软件求生”获取更多技术内容!
922 0
|
编解码 Android开发
常用adb命令
常用adb命令
471 2
|
算法 Serverless 计算机视觉
Fast Fourier Transform,简称 FFT
快速傅里叶变换(Fast Fourier Transform,简称 FFT)是一种高效计算离散傅里叶变换(DFT)的算法。它可以将一个有限长度的离散信号序列转换为一系列不同频率的正弦和余弦波,从而使我们能够更容易地分析和处理信号。与传统的 DFT 算法相比,FFT 算法具有更高的计算效率,因为它利用了对称性和周期性的性质,将计算复杂度从 O(N^2) 降低到 O(NlogN)。
583 5
|
消息中间件 Kafka
kafka写入和消费流程
kafka写入和消费流程
406 0
|
Android开发
Android自定义View水波纹
Android自定义View水波纹
Android自定义View水波纹
|
Unix Linux 调度
【操作系统篇】第四篇——线程(概念,实现方式,模型,状态与转换)
【操作系统篇】第四篇——线程(概念,实现方式,模型,状态与转换)
【操作系统篇】第四篇——线程(概念,实现方式,模型,状态与转换)

热门文章

最新文章