跳转至

多线程编程:线程通信

在多线程编程中,线程之间的通信是一个非常重要的概念。线程通信允许线程之间共享数据、协调任务以及同步操作。Java提供了多种机制来实现线程通信,包括wait()notify()notifyAll()方法以及BlockingQueue等工具类。本文将详细介绍这些机制,并通过代码示例帮助你理解它们的使用方法。

1. 线程通信的基本概念

在多线程环境中,线程之间可能需要共享数据或协调任务。为了实现这一点,线程之间需要进行通信。Java提供了以下几种主要的线程通信机制:

  • wait()notify() 方法:这些方法用于在对象监视器上进行线程等待和唤醒操作。
  • BlockingQueue:这是一个线程安全的队列,支持阻塞操作,常用于生产者-消费者模型。
  • Condition 接口:与Lock配合使用,提供了更灵活的线程通信机制。

2. 使用 wait()notify() 进行线程通信

wait()notify()Object类中的方法,用于在对象监视器上进行线程通信。wait()方法使当前线程进入等待状态,直到其他线程调用notify()notifyAll()方法唤醒它。

2.1 代码示例:生产者-消费者模型

以下是一个简单的生产者-消费者模型的示例,使用wait()notify()方法实现线程通信。

class SharedResource {
    private int data;
    private boolean available = false;

    public synchronized void produce(int value) {
        while (available) {
            try {
                wait(); // 等待消费者消费
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        data = value;
        available = true;
        System.out.println("Produced: " + data);
        notify(); // 通知消费者可以消费了
    }

    public synchronized int consume() {
        while (!available) {
            try {
                wait(); // 等待生产者生产
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        available = false;
        System.out.println("Consumed: " + data);
        notify(); // 通知生产者可以生产了
        return data;
    }
}

class Producer extends Thread {
    private SharedResource resource;

    public Producer(SharedResource resource) {
        this.resource = resource;
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            resource.produce(i);
        }
    }
}

class Consumer extends Thread {
    private SharedResource resource;

    public Consumer(SharedResource resource) {
        this.resource = resource;
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            resource.consume();
        }
    }
}

public class ProducerConsumerExample {
    public static void main(String[] args) {
        SharedResource resource = new SharedResource();
        Producer producer = new Producer(resource);
        Consumer consumer = new Consumer(resource);

        producer.start();
        consumer.start();
    }
}

2.2 代码解释

  • SharedResource:这是一个共享资源类,包含produce()consume()方法。produce()方法用于生产数据,consume()方法用于消费数据。
  • wait()方法:当资源不可用时,线程调用wait()方法进入等待状态。
  • notify()方法:当资源可用时,线程调用notify()方法唤醒等待的线程。

3. 使用 BlockingQueue 进行线程通信

BlockingQueue是一个线程安全的队列,支持阻塞操作。它常用于生产者-消费者模型,简化了线程通信的实现。

3.1 代码示例:使用 BlockingQueue 实现生产者-消费者模型

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

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

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

    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                queue.put(i); // 生产数据并放入队列
                System.out.println("Produced: " + i);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

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

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

    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                int value = queue.take(); // 从队列中消费数据
                System.out.println("Consumed: " + value);
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

public class BlockingQueueExample {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
        Thread producer = new Thread(new Producer(queue));
        Thread consumer = new Thread(new Consumer(queue));

        producer.start();
        consumer.start();
    }
}

3.2 代码解释

  • BlockingQueue:这是一个线程安全的队列,支持阻塞操作。put()方法在队列满时会阻塞,take()方法在队列空时会阻塞。
  • 生产者:生产者线程调用put()方法将数据放入队列。
  • 消费者:消费者线程调用take()方法从队列中取出数据。

4. 使用 Condition 进行线程通信

Condition接口与Lock配合使用,提供了更灵活的线程通信机制。Condition允许线程在某些条件下等待,并在条件满足时被唤醒。

4.1 代码示例:使用 Condition 实现生产者-消费者模型

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class SharedResource {
    private int data;
    private boolean available = false;
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void produce(int value) {
        lock.lock();
        try {
            while (available) {
                condition.await(); // 等待消费者消费
            }
            data = value;
            available = true;
            System.out.println("Produced: " + data);
            condition.signal(); // 通知消费者可以消费了
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public int consume() {
        lock.lock();
        try {
            while (!available) {
                condition.await(); // 等待生产者生产
            }
            available = false;
            System.out.println("Consumed: " + data);
            condition.signal(); // 通知生产者可以生产了
            return data;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return -1;
        } finally {
            lock.unlock();
        }
    }
}

class Producer extends Thread {
    private SharedResource resource;

    public Producer(SharedResource resource) {
        this.resource = resource;
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            resource.produce(i);
        }
    }
}

class Consumer extends Thread {
    private SharedResource resource;

    public Consumer(SharedResource resource) {
        this.resource = resource;
    }

    public void run() {
        for (int i = 0; i < 5; i++) {
            resource.consume();
        }
    }
}

public class ConditionExample {
    public static void main(String[] args) {
        SharedResource resource = new SharedResource();
        Producer producer = new Producer(resource);
        Consumer consumer = new Consumer(resource);

        producer.start();
        consumer.start();
    }
}

4.2 代码解释

  • LockConditionLock用于控制对共享资源的访问,Condition用于线程之间的通信。
  • await()方法:使当前线程等待,直到其他线程调用signal()signalAll()方法。
  • signal()方法:唤醒等待的线程。

5. 练习题

5.1 简单练习

  1. 修改生产者-消费者模型的代码,使得生产者生产10个数据,消费者消费10个数据。

5.2 中等练习

  1. 使用BlockingQueue实现一个多生产者-多消费者的模型,确保每个生产者生产的数据都能被消费者正确消费。

5.3 复杂练习

  1. 使用Condition实现一个读写锁模型,允许多个读线程同时访问共享资源,但写线程独占访问。

6. 总结

  • 线程通信是多线程编程中的重要概念,用于线程之间的数据共享和任务协调。
  • wait()notify() 是基本的线程通信机制,适用于简单的生产者-消费者模型。
  • BlockingQueue 提供了线程安全的队列,简化了生产者-消费者模型的实现。
  • ConditionLock配合使用,提供了更灵活的线程通信机制。

通过掌握这些线程通信机制,你可以编写出高效、可靠的多线程程序。