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();
    }
}


相关文章
|
2月前
|
安全 Java 编译器
高并发编程之什么是 JUC
高并发编程之什么是 JUC
26 1
|
10月前
|
Java
Java并发编程:使用CountDownLatch实现线程同步
在Java并发编程中,线程同步是一个重要的概念。当多个线程需要协调工作或者共享资源时,我们需要使用一些机制来确保线程的有序执行。
135 1
|
19天前
|
安全 算法 Java
|
2月前
|
缓存 Java 编译器
关于Java并发多线程的一点思考
关于Java并发多线程的一点思考
46 1
|
9月前
|
安全 Java 调度
JUC并发编程(上)
JUC并发编程(上)
45 0
|
9月前
|
存储 缓存 监控
JUC并发编程(下)
JUC并发编程(下)
28 0
|
10月前
|
存储 消息中间件 算法
多线程和并发编程(4)—并发数据结构之BlockingQueue
多线程和并发编程(4)—并发数据结构之BlockingQueue
61 0
|
并行计算 Java 应用服务中间件
JUC并发编程超详细详解篇(一)
JUC并发编程超详细详解篇
1455 1
JUC并发编程超详细详解篇(一)
|
11月前
|
缓存 监控 安全
JUC并发编程之线程锁(一)
1.ReentrantLock(互斥锁)、2.ReentRantReaderWriterLock(互斥读写锁)、3.StampedLock(无障碍锁)、4.Condition(自定义锁)、5.LockSupport
63 0
|
安全 Java 编译器
JUC 并发编程
JUC 并发编程