目录
LockSupport位于java.util.concurrent.locks包下,可以用来实现线程的阻塞和唤醒操作。每个使用LockSupport的线程都会与一个许可关联,如果该许可可用,并且可在线程中使用,则调用park()将会立即返回,否则可能阻塞。如果许可尚不可用,则可以调用unpark()方法使其可用。但是注意许可不可重入,也就是说只能调用一次park()方法,否则会一直阻塞。
LockSupport类提供了阻塞和唤醒线程的方法,主要有以下几个方法:
阻塞线程
唤醒线程
需要注意的是,LockSupport的阻塞和唤醒线程功能是依赖于sun.misc.Unsafe类实现的。在阻塞线程方法中,可以使用带Object参数的重载方法来记录导致线程阻塞的阻塞对象,这有助于问题排查和定位。
相较于传统的synchronized等同步方式,LockSupport的线程阻塞不会引发死锁和竞争条件问题,并且可以精确地控制线程的阻塞和唤醒。当线程被阻塞时,其状态会变为WAITING;而使用synchronized等方式阻塞线程时,线程的状态则会变为BLOCKED。
以下是一个示例,展示了如何使用 LockSupport 和条件变量(Condition)来实现生产者-消费者模式:
- import java.util.LinkedList;
- import java.util.Queue;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
- import java.util.concurrent.locks.LockSupport;
-
- public class main {
- private static final int CAPACITY = 5;
- private static Queue
queue = new LinkedList<>(); - private static Lock lock = new ReentrantLock();
- private static Condition notFull = lock.newCondition();
- private static Condition notEmpty = lock.newCondition();
-
- public static void main(String[] args) {
- Thread producer = new Thread(() -> {
- for (int i = 0; i < 10; i++) {
- lock.lock();
- try {
- while (queue.size() == CAPACITY) {
- notFull.await();
- }
- queue.offer(i);
- System.out.println("生产者生产:" + i);
- notEmpty.signalAll();
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- });
-
- Thread consumer = new Thread(() -> {
- while (true) {
- lock.lock();
- try {
- while (queue.isEmpty()) {
- notEmpty.await();
- }
- int item = queue.poll();
- System.out.println("消费者消费:" + item);
- notFull.signalAll();
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- lock.unlock();
- }
- }
- });
-
- producer.start();
- consumer.start();
-
- // 让主线程等待一段时间
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- // 停止生产者和消费者线程
- LockSupport.unpark(producer);
- LockSupport.unpark(consumer);
- }
- }
在以上代码中,queue 是一个用于存储数据的队列,lock 是一个可重入锁,notFull 和 notEmpty 分别是条件变量,用于控制生产者和消费者的等待和唤醒操作。
生产者线程在每次生产之前,使用 lock.lock() 获取锁并进入临界区,然后通过 while 循环检查队列是否已满。如果队列已满,则调用 notFull.await() 将生产者线程阻塞,直到有消费者消费数据并唤醒生产者线程。一旦队列有空位,生产者将数据放入队列,并调用 notEmpty.signalAll() 唤醒等待的消费者线程。最后,释放锁并结束本次生产。
消费者线程在每次消费之前,也需要先获取锁并进入临界区,然后通过 while 循环检查队列是否为空。如果队列为空,则调用 notEmpty.await() 将消费者线程阻塞,直到有生产者生产数据并唤醒消费者线程。一旦队列有数据,消费者从队列中取出数据,并调用 notFull.signalAll() 唤醒等待的生产者线程。最后,释放锁并继续下一轮消费。
主线程启动生产者和消费者线程后,等待一段时间后调用 LockSupport.unpark(Thread) 方法来停止生产者和消费者线程。
这个示例展示了如何使用 LockSupport 和条件变量实现生产者-消费者模式,确保生产者在队列满时等待,消费者在队列空时等待,并通过条件变量进行线程的唤醒操作。这种方式可以有效地保证生产者和消费者的同步和互斥,避免了普通的忙等待和竞争条件,提高了系统的效率和可靠性。