Producer Consumer problem - mutex and semaphore

很典型的一道多线程题目。如果使用BlockingQueue,就十分简单。但是要注意blockingqueue的语法,用put/take

package concurrent;

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

public class ProducerConsumerBlockingQueue {
    static BlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();

    public static void main(String[] args) {
        new Thread(new Producer()).start();
        new Thread(new Consumer()).start();
    }

    private static class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    queue.put(i);
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                }
                System.out.println("queue offer " + i);
            }
        }
    }

    private static class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("queue.take " + queue.take());
                System.out.println("queue.take " + queue.take());
                System.out.println("queue.take " + queue.take());
                System.out.println("queue.take " + queue.take());
                System.out.println("queue.take " + queue.take());
            } catch (InterruptedException e) {
            }
        }
    }

}


下面是自己实现的两个线程,使用mutex 方法 wait() and notify()

package concurrent;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;

public class ProducerConsumer {
    static Queue<Integer> queue = new LinkedList<Integer>();

    public static void main(String[] args) {
        new Thread(new Producer()).start();
        new Thread(new Consumer()).start();
    }

    private static class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                while (queue.size() > 1) {
                    try {
                        queue.wait();
                    } catch (Exception e) {
                    }
                }
                synchronized (queue) {
                    queue.offer(i);
                    System.out.println("queue offer " + i);
                    queue.notify();
                }
            }
        }
    }

    private static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.isEmpty()) {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                    int i = queue.poll();
                    System.out.println("queue poll " + i);
                    queue.notify();
                }
            }
        }
    }
}

输出:

queue offer 0
queue offer 1
queue poll 0
queue poll 1
queue offer 2
queue offer 3
queue poll 2
queue poll 3
queue offer 4
queue offer 5
queue poll 4
queue poll 5
queue offer 6
queue offer 7
queue poll 6
queue poll 7
queue offer 8
queue offer 9
queue poll 8
queue poll 9

几个需要注意的地方:

1. 因为两个Thread都对queue进行操作,所有读写操作都需要用synchronizd关键字保证mutex

2. 在Producer里面我们有一个地方是while(queue.size()>1)。这么做是因为我想生产者消费者轮流放入和取出。如果没有这个while循环,生产者会任意生产10个数,而消费者取出的顺序也会随机。

比如:

queue offer 0
queue poll 0
queue offer 1
queue poll 1
queue offer 2
queue poll 2
queue offer 3
queue poll 3
queue offer 4
queue offer 5
queue offer 6
queue offer 7
queue offer 8
queue offer 9
queue poll 4
queue poll 5
queue poll 6
queue poll 7
queue poll 8
queue poll 9

用信号量实现同样的功能就简单了许多,不再使用synchronized关键字,不需要while循环。

为什么呢? 因为第一种方法实现的是monitor,使用busy wait,他会不断的check;而信号量就像红绿灯一样,一个thread通知下一个thread,下一个thread才可以执行,所以两个thread都不需要busy wait或者不可能有两个thread同时操作一个变量的情况。

package concurrent;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;

public class ProducerConsumerSemaphore {
    static Queue<Integer> queue = new LinkedList<Integer>();
    static Semaphore semaphore_consume = new Semaphore(0);
    static Semaphore semaphore_produce = new Semaphore(1);

    public static void main(String[] args) {
        new Thread(new Producer()).start();
        new Thread(new Consumer()).start();
    }

    private static class Producer implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    semaphore_produce.acquire();
                } catch (InterruptedException e) {
                }
                queue.offer(i);
                System.out.println("queue offer " + i);
                semaphore_consume.release();
            }
        }
    }

    private static class Consumer implements Runnable {
        @Override
        public void run() {
            while (true) {
                try {
                    semaphore_consume.acquire();
                } catch (InterruptedException e) {
                }
                int i = queue.poll();
                System.out.println("queue poll " + i);
                semaphore_produce.release();
            }
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
The producer-consumer problem is a classic synchronization problem in computer science. It involves two processes, a producer and a consumer, who share a common buffer. The producer puts items into the buffer, while the consumer takes items out of the buffer. The problem arises when the buffer is full, and the producer needs to wait until the consumer has consumed some items before it can add more, and when the buffer is empty, and the consumer needs to wait until the producer has produced some items before it can consume them. One way to solve this problem is by using semaphores or locks. Semaphores are integer variables that can be used to control access to shared resources. They have two fundamental operations, wait() and signal(). The wait() operation decrements the semaphore value, and if it is negative, it blocks the process until the value becomes positive. The signal() operation increments the semaphore value and unblocks any waiting processes. A solution using semaphores for the producer-consumer problem involves two semaphores, empty and full, and a mutex lock. The empty semaphore is initialized to the size of the buffer, while the full semaphore is initialized to 0. The mutex lock is used to ensure that only one process can access the buffer at a time. The producer process waits on the empty semaphore, acquires the mutex lock, adds an item to the buffer, releases the mutex lock, and signals the full semaphore. The consumer process waits on the full semaphore, acquires the mutex lock, removes an item from the buffer, releases the mutex lock, and signals the empty semaphore. Here is some sample pseudocode: ``` // Shared variables int buffer[N]; int count = 0; // number of items in buffer int in = 0; // index for producer to put items int out = 0; // index for consumer to take items // Semaphores Semaphore empty = N; Semaphore full = 0; // Mutex lock Lock mutex; // Producer code while (true) { produce_item(); empty.wait(); mutex.acquire(); buffer[in] = item; in = (in + 1) % N; count++; mutex.release(); full.signal(); } // Consumer code while (true) { full.wait(); mutex.acquire(); item = buffer[out]; out = (out + 1) % N; count--; mutex.release(); empty.signal(); consume_item(item); } ``` This solution ensures that the producer and consumer processes do not access the buffer at the same time, and that the buffer is not overfilled or underfilled.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值