生产者和消费者的Java多线程实现

本文探讨了如何使用Java实现一个线程安全的缓冲区(Buffer),通过生产者(Producer)和消费者(Consumer)线程进行数据的生产与消费,确保队列的满载与空闲状态下的同步与协作。关键部分展示了队列设计、生产者和消费者线程的实现,以及测试案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现目标

我们设定一定空间的队列,由生产者进行生产(填充队列)和消费者进行消费(取队列数据)。生产者在队列满的时候需要等待,消费者在队列空的时候需要等待。同时保证多个生产者、消费者的线程安全和数据协同。

 

队列的设计

public class Buffer<T> {

    private T[] data;
    private int size;
    private boolean empty;
    private int reader;
    private int writer;

    public boolean isEmpty() {
        return reader == writer;
    }

    public boolean isFull(){
        return (writer + 1) % size == reader;
    }

    public int size(){
        return (writer + size - reader) % size;
    }

    public Buffer(Class clazz, int size) {
        this.data = (T[]) Array.newInstance(clazz, size + 1);
        this.size = size + 1;
        this.reader = 0;
        this.writer = 0;
        this.empty = true;
    }

    public boolean push(T ele){
        if(isFull()){
            return false;
        }
        data[writer] = ele;
        writer = (++writer) % size;
        return true;
    }

    public T pop(){
        if(isEmpty()){
            return null;
        }
        T val = data[reader];
        reader = (++reader) % size;
        return val;
    }
}

这里使用循环数组实现了一个队列。这里设置了两个指针:读指针和写指针。读指针用于取出数据,写指针用于压入数据。取出数据之前先做队列非空判断,压入数据之前先做队列非满判断。

 

生产者设计

public class Producer implements Runnable{

    private final Buffer<Integer> buffer;

    public Producer(Buffer<Integer> buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            while (true){
                synchronized (buffer){
                    while (buffer.isFull()){
                        buffer.wait();
                    }
                    int random = new Random().nextInt(100);
                    buffer.push(random);
                    System.out.println(Thread.currentThread().getName() + ":" + buffer.size());
                    buffer.notify();
                }
            }
        }catch (Exception e) {
            System.err.println("hit Exception" + e);
        }
    }
}

 

生产者在一个while循环中反复执行以下操作:

1. 获取Buffer对象的对象锁。

2. 判断队列非满,如果满了调用对象锁的wait方法,进入等待池。

3. 队列非满的情况下,将数据添加到队列。

4. 通知其他线程当前可用。

 

消费者设计

 

public class Consumer implements Runnable{

    private final Buffer<Integer> buffer;

    public Consumer(Buffer<Integer> buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            while (true){
                Thread.sleep(1000);
                synchronized (buffer){
                    while (buffer.isEmpty()){
                        buffer.wait();
                    }
                    int val = buffer.pop();
                    System.out.println(Thread.currentThread().getName() + ":" + buffer.size());
                    buffer.notify();
                }
            }
        }catch (Exception e) {
            System.err.println("hit Exception" + e);
        }
    }
}

消费者在一个while循环中反复执行以下操作:

1. 获取Buffer对象的对象锁。

2. 判断队列非空,如果空了调用对象锁的wait方法,进入等待池。

3. 队列非空的情况下,将数据从队列中取出。

4. 通知其他线程当前可用。

 

测试

public class Test {
    public static void main(String[] args) {
        Buffer<Integer> buffer = new Buffer<Integer>(Integer.class, 5);
        for (int i = 0; i < 5; i++) {
            new Thread(new Producer(buffer), "producer" + i).start();
        }

        for (int i = 0; i < 10; i++) {
            new Thread(new Consumer(buffer), "consumer" + i).start();
        }
    }
}

 

补充:查看当前执行线程

Thread.currentThread()方法可以获取当前执行的线程。

package com.jdojo.threads;

public class CurrentThread extends Thread {
        
       public CurrentThread(String name) {
                super(name);
       }

       @Override
       public void run() {
            Thread t = Thread.currentThread();
            String threadName = t.getName(); 
            System.out.println("Inside run() method: " + threadName);
       }

        public static void main(String[] args) {
            CurrentThread ct1 = new CurrentThread("First Thread"); 
            CurrentThread ct2 = new CurrentThread("Second Thread"); 
            ct1.start();
            ct2.start();

            // Let’s see which thread is executing the following statement 
            Thread t = Thread.currentThread();
            String threadName = t.getName();
            System.out.println("Inside main() method: " + threadName);
        } 
}

 

线程睡眠

 

睡眠线程是不会被分配CPU的时间片的,如果线程在睡眠之前已经占用了锁,睡眠期间他将继续占用锁。

package com.jdojo.threads;

public class LetMeSleep {
        public static void main(String[] args) {
            try {
                System.out.println("I am going to sleep for 5 seconds.");             
                Thread.sleep(5000); 
                // The "main" thread will sleep 
                System.out.println("I woke up.");
            }
            catch(InterruptedException e) {
                System.out.println("Someone interrupted me in my sleep."); 
                System.out.println("I am done.");
            } 
        }
}

 

等你等到死

执行某线程的join方法,执行的线程将停下等待,直到被调用join的线程执行完毕。

package com.jdojo.threads;

public class JoinRight {
        public static void main(String[] args) {
            Thread t1 = new Thread(JoinRight::print); 
            t1.start();
            try {
                t1.join(); // "main" thread waits until t1 is terminated
            }
            catch (InterruptedException e) {
                e.printStackTrace();    
            } 
            System.out.println("We are done.");
        }

        public static void print() {
             for (int i = 1; i <= 5; i++) {
                try {
                    System.out.println("Counter: " + i); 
                    Thread.sleep(1000);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值