江明涛的博客
Java 线程的阻塞与唤醒
Java 线程的阻塞与唤醒

Java 线程的阻塞与唤醒

Java 线程的阻塞与唤醒

在Java多线程编程中,线程的阻塞与唤醒是非常重要的概念。线程的阻塞意味着线程的执行暂时停止,而线程的唤醒则是将一个阻塞的线程恢复到可运行状态。

Java为我们提供了多种机制来实现线程的阻塞与唤醒,下面将介绍几种常用的方式。

1. 使用wait()和notify()方法

在Java中,每个对象都有一个等待队列,线程可以通过调用对象的wait()方法进入等待状态。wait()方法会使线程释放对象的锁,并将线程放入等待队列中,直到其他线程调用对象的notify()或notifyAll()方法来唤醒等待队列中的线程。

synchronized (obj) {
    // 进入临界区
    try {
        // 当条件不满足时,线程进入等待状态
        obj.wait();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    // 被唤醒后继续执行
}

上述代码中,synchronized关键字用于获取对象的锁,保证线程安全。线程在进入临界区后,如果条件不满足,就调用wait()方法进入等待状态,直到其他线程调用notify()方法唤醒它。唤醒后,线程将继续执行。

2. 使用Lock和Condition

Java提供的Lock和Condition是java.util.concurrent包提供的高级线程控制机制。与synchronized关键字不同,Lock是显示锁,需要手动释放锁,而Condition可以为每个对象维护多个等待队列,并且可以精确地唤醒指定的线程。

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
try {
    // 当条件不满足时,线程进入等待状态
    condition.await();
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    // 唤醒等待队列中的一个线程
    condition.signal();
    lock.unlock();
}

上述代码中,首先通过lock()方法获取锁,然后调用await()方法进入等待状态。当其他线程调用signal()方法时,会从等待队列中唤醒一个线程继续执行。最后,需要调用unlock()方法释放锁。

3. 使用阻塞队列

阻塞队列也是Java提供的一种线程同步机制,它可以阻塞线程的入队和出队操作。如果队列为空,则出队操作会阻塞线程,直到队列非空;如果队列已满,则入队操作会阻塞线程,直到队列有空闲位置。

BlockingQueue<Task> queue = new ArrayBlockingQueue<>(10);
// 生产者线程
threadPool.execute(() -> {
    try {
        Task task = produceTask();
        queue.put(task); // 阻塞入队
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
// 消费者线程
threadPool.execute(() -> {
    try {
        Task task = queue.take(); // 阻塞出队
        consumeTask(task);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});

上述代码中,生产者线程通过put()方法向队列中放入任务,如果队列已满,则会阻塞等待;消费者线程通过take()方法从队列中取出任务,如果队列为空,则会阻塞等待。阻塞队列为我们提供了一种简便的方式来管理线程的阻塞和唤醒。

综上所述,Java提供了多种机制来实现线程的阻塞与唤醒,包括使用wait()和notify()方法、Lock和Condition、阻塞队列等。在多线程编程中,合理地使用这些机制可以更好地管理线程的执行顺序和并发控制。