Java线程如何阻塞

在Java编程中,线程的阻塞是一个常见的问题。线程阻塞可以发生在多种情况下,例如等待I/O操作完成、等待其他线程的锁释放等。本文将介绍Java中线程如何被阻塞,以及如何解决这个问题。

问题描述

假设有一个简单的需求:实现一个生产者-消费者模型,其中生产者生产产品放入一个共享队列中,消费者从队列中取出产品进行消费。当队列为空时,消费者需要等待生产者生产产品放入队列;当队列满时,生产者需要等待消费者取出产品。

解决方案

Java提供了多种方式来实现线程的阻塞,其中比较常用的有使用wait()和notify()/notifyAll()方法,以及使用BlockingQueue。

使用wait()和notify()/notifyAll()方法

class SharedQueue {
    private List<Integer> queue = new ArrayList<>();
    private int capacity = 10;

    public synchronized void produce(int item) {
        while (queue.size() == capacity) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        queue.add(item);
        notify();
    }

    public synchronized int consume() {
        while (queue.isEmpty()) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        int item = queue.remove(0);
        notify();
        return item;
    }
}

在上面的代码中,SharedQueue类表示共享队列,produce()方法用于生产产品,consume()方法用于消费产品。当队列满或为空时,对应的线程会调用wait()方法将自己阻塞,等待其他线程唤醒。

使用BlockingQueue

class Producer implements Runnable {
    private BlockingQueue<Integer> queue;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                queue.put(i);
                System.out.println("Produced: " + i);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Consumer implements Runnable {
    private BlockingQueue<Integer> queue;

    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            while (true) {
                int item = queue.take();
                System.out.println("Consumed: " + item);
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
        ExecutorService executor = Executors.newCachedThreadPool();
        executor.execute(new Producer(queue));
        executor.execute(new Consumer(queue));
        executor.shutdown();
    }
}

在上面的代码中,使用BlockingQueue可以更方便地实现生产者-消费者模型,无需手动处理线程的阻塞和唤醒。

类图

classDiagram
    class SharedQueue {
        -List<Integer> queue
        -int capacity
        +produce(int)
        +consume():int
    }

    class Producer {
        -BlockingQueue<Integer> queue
        +run()
    }

    class Consumer {
        -BlockingQueue<Integer> queue
        +run()
    }

    class Main {
        +main(String[])
    }

    SharedQueue <|-- Producer
    SharedQueue <|-- Consumer
    Main --> Producer
    Main --> Consumer

结论

通过使用wait()和notify()/notifyAll()方法或者BlockingQueue,可以有效地解决Java线程阻塞的问题。在实际开发中,根据具体情况选择合适的方式来实现线程同步和通信,可以提高程序的性能和可维护性。