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