Java阻塞线程的概念及使用场景
在Java多线程编程中,阻塞线程是一种常见的情况。当线程遇到某些情况时,无法继续执行,会进入阻塞状态,直到满足特定条件才能继续执行。阻塞线程的使用场景包括等待输入、等待网络连接、等待锁、等待条件满足等。
1. 阻塞线程的原因
线程进入阻塞状态的原因有很多,包括但不限于:
- 线程调用
Thread.sleep(long millis)
方法,主动进入睡眠状态,在指定的时间后恢复执行。 - 线程调用
wait()
方法,让出对象锁,进入等待状态,直到被其它线程调用notify()
或notifyAll()
方法唤醒。 - 线程调用
join()
方法,等待另一个线程执行完毕后再继续执行。 - 线程等待I/O操作完成,如读写文件、网络通信等。
- 线程等待获取锁资源,如果锁已经被其它线程占用。
2. 使用示例
以一个简单的生产者-消费者模型为例,说明Java阻塞线程的使用。
import java.util.LinkedList;
class ProducerConsumer {
private LinkedList<Integer> buffer = new LinkedList<>();
private final int MAX_SIZE = 10;
public void produce() throws InterruptedException {
synchronized (this) {
while (buffer.size() == MAX_SIZE) {
// 缓冲区已满,生产者线程等待
this.wait();
}
int value = 1; // 生产一个新值
buffer.add(value);
System.out.println("Produced: " + value);
this.notifyAll(); // 唤醒等待的消费者线程
}
}
public void consume() throws InterruptedException {
synchronized (this) {
while (buffer.isEmpty()) {
// 缓冲区为空,消费者线程等待
this.wait();
}
int value = buffer.removeFirst();
System.out.println("Consumed: " + value);
this.notifyAll(); // 唤醒等待的生产者线程
}
}
}
public class Main {
public static void main(String[] args) {
ProducerConsumer pc = new ProducerConsumer();
Thread producerThread = new Thread(() -> {
while (true) {
try {
Thread.sleep(1000); // 模拟生产耗时
pc.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumerThread = new Thread(() -> {
while (true) {
try {
Thread.sleep(2000); // 模拟消费耗时
pc.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producerThread.start();
consumerThread.start();
}
}
在上述示例中,生产者线程和消费者线程共享一个缓冲区buffer
,当缓冲区为空时,消费者线程进入阻塞状态等待生产者线程生产新的值。当缓冲区满时,生产者线程进入阻塞状态等待消费者线程消费值。在生产者和消费者之间通过synchronized
实现互斥访问缓冲区,并使用wait()
和notifyAll()
方法进行线程的阻塞和唤醒操作。
3. Java线程状态与阻塞线程的监控
在Java中,可以通过Thread.getState()
方法来获取线程的状态。常见的线程状态包括NEW
、RUNNABLE
、BLOCKED
、WAITING
、TIMED_WAITING
和TERMINATED
。
NEW
:新创建的线程,还没有开始执行。RUNNABLE
:在Java虚拟机中执行的线程。BLOCKED
:被阻塞等待监视器锁定的线程。WAITING
:无限期地等待另一个线程执行特定操作的线程。TIMED_WAITING
:等待另一个线程执行操作,最多等待一段时间。