So I have a Producer, Consumer and a shared Synchronized buffer.
The producer creates a number (that gets saved in the buffer)
And the consumer gets 5 guesses to guess the number.
Once the consumer guesses correctly it’s asked whether it wants to play again and number of times guessed correctly is saved in the buffer.
My synchronizedBuffer class is all wrong. I don't even know where to begin to implement the second value into it. Even a little hint as to how I'm supposed to do it will be much appreciated. I'm not allowed to use array-blocking queues.
//Class: Producer
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class Producer implements Runnable {
private final static Random generator = new Random();
private final Buffer sharedLocation;
public Producer(Buffer shared) {
sharedLocation = shared;
}
public void run() {
try {
int x = ThreadLocalRandom.current().nextInt(1, 10);
Thread.sleep(generator.nextInt(3000)); // random sleep
sharedLocation.set(x); // set value in buffer
System.out.printf("\n", x);
}
catch (InterruptedException exception) {
exception.printStackTrace();
}
System.out.println("Producer done producing");
}
}
//Class: Consumer
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class Consumer implements Runnable {
private final static Random generator = new Random();
private final Buffer sharedLocation;
public Consumer(Buffer shared) {
sharedLocation = shared;
}
public void run() {
int correct = 0;
outerloop: for (int i = 1; i <= 5; i++) {
try {
int x = ThreadLocalRandom.current().nextInt(1, 10);
Thread.sleep(generator.nextInt(2000));
if (x == sharedLocation.get()) {
System.out.println(x);
System.out.println("correct guess was " + x);
correct++;
sharedLocation.set(correct);
break outerloop;
}
System.out.print(x + "\n");
} catch (InterruptedException exception) {
exception.printStackTrace();
}
}
}
}
public class SynchronizedBuffer implements Buffer {
private int buffer = -1;
private boolean occupied = false;
public synchronized void set(int value) throws InterruptedException {
while (occupied) {
System.out.println("Producer tries to write.");
System.out.println("Consumer tries to guess");
wait();
} // end while
buffer = value;
occupied = true;
displayState("Producer writes " + buffer);
notifyAll();
buffer = value;
}
public synchronized int get() throws InterruptedException {
while (!occupied) {
System.out.println("Consumer tries to guess.");
displayState("Buffer empty. Consumer waits.");
wait();
}
occupied = false;
notifyAll();
return buffer;
}
public void displayState(String operation) {
System.out.printf("%-40s%d\t\t%b\n", operation, buffer, occupied);
}
}
Related
I am trying to use two threads to alternate between each other to count to a certain random number. For example, if thread A is assigned with a random number 4 and thread B is assigned with a random number 7, I am looking for output like this:
Thread A:1
Thread B:1
Thread A:2
Thread B:2
Thread A:3
Thread B:3
Thread A:4
Thread B:4
Thread A:5
Thread A:6
Thread A:7
So basically it will switch between threads and if one thread is done counting to the random number it will stop and let the other thread finish counting to the random number. This is the code I have so far:
public class Test extends Thread {
public static Object sync = new Object();
public void run() {
Random rand = new Random();
int random = rand.nextInt(20) + 1; // generate random number 1-20
for (int i = 0; i < random; i++) {
synchronized (sync) {
try {
sync.notify();
System.out.println(i);
sync.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
I have been at a dead end for a while and I have no idea on what to do. This is my first time using threads so I could use some help. If anyone knows how to do this let me know as I have tried so much but nothing has worked. Thanks in advance.
This is a little, basic. I'm not a fan of static in this context, but for a demonstration, it should help prove the point
Essentially, you need a "counter" which can keep track of the number of "active" threads, if there is only one, you don't need to "wait", for example...
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.DataFormatException;
public class Main {
public static void main(String[] args) throws IOException, DataFormatException {
new Thread(new Test(), "One").start();
new Thread(new Test(), "Two").start();
}
public static class Test implements Runnable {
public static Object sync = new Object();
public static AtomicInteger counter = new AtomicInteger(0);
public void run() {
counter.incrementAndGet();
Random rand = new Random();
// generate random number 1-20;
int random = rand.nextInt((20 - 1) + 1) + 1;
String name = Thread.currentThread().getName();
System.out.println(name + " count to " + random);
for (int i = 0; i < random; i++) {
synchronized (sync) {
try {
sync.notify();
System.out.println(name + " " + i);
int count = counter.get();
if (count > 1) {
sync.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
counter.decrementAndGet();
synchronized (sync) {
sync.notify();
}
}
}
}
And, because I don't like to abuse static (sync in this context could be argued, but as demonstration of the basic concept)
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.zip.DataFormatException;
public class Main {
public static void main(String[] args) throws IOException, DataFormatException {
Object lock = new Object();
AtomicInteger counter = new AtomicInteger(0);
new Thread(new Test(lock, counter), "One").start();
new Thread(new Test(lock, counter), "Two").start();
}
public static class Test implements Runnable {
private Object lock;
private AtomicInteger counter;
public Test(Object lock, AtomicInteger counter) {
this.lock = lock;
this.counter = counter;
}
public void run() {
counter.incrementAndGet();
Random rand = new Random();
// generate random number 1-20;
int random = rand.nextInt((20 - 1) + 1) + 1;
String name = Thread.currentThread().getName();
System.out.println(name + " count to " + random);
for (int i = 0; i < random; i++) {
synchronized (lock) {
try {
lock.notify();
System.out.println(name + " " + i);
int count = counter.get();
if (count > 1) {
lock.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
counter.decrementAndGet();
synchronized (lock) {
lock.notify();
}
}
}
}
new to multithreading. I wrote this program which should be a solution to the producer-consumer problem. The problem is that both a producer and a consumer end up in the waiting state. What seems to be wrong? (And everything else what is wrong ^_^) Thanks in advance.
Main class:
package producer.consumer2;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Buffer<Integer> bf = new Buffer<>(10);
Producer prod = new Producer(bf);
Consumer cons = new Consumer(bf);
prod.setConsumer(cons);
cons.setProducer(prod);
new Thread(prod).start();
new Thread(cons).start();
if(quitInput()) {
prod.terminate();
cons.terminate();
}
}
private static boolean quitInput() {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
do {
if(line.toLowerCase().equals("q") || line.toLowerCase().equals("quit")) {
sc.close();
return true;
}
line = sc.nextLine();
} while(true);
}
}
Buffer class:
package producer.consumer2;
import java.util.ArrayList;
public class Buffer<E> {
private final int MAX_LENGTH;
private ArrayList<E> values;
public Buffer(int length){
MAX_LENGTH = length;
values = new ArrayList<E>(length);
}
public synchronized void add(E e) {
if(values.size() < MAX_LENGTH) {
values.add(e);
System.out.println(values);
} else {
throw new RuntimeException("Buffer is full at the moment.");
}
}
public synchronized boolean isEmpty() {
return values.size() == 0;
}
public synchronized boolean isFull() {
return values.size() >= MAX_LENGTH ? true : false;
}
public synchronized E remove(int index) {
E val = values.remove(index);
System.out.println(values);
return val;
}
}
Consumer class:
package producer.consumer2;
public class Consumer implements Runnable {
private final Buffer<Integer> bf;
private volatile boolean running = true;
private Producer prod;
public Consumer(Buffer<Integer> bf) {
this.bf = bf;
}
public void setProducer(Producer prod) {
this.prod = prod;
}
#Override
public void run() {
int sum = 0;
int counter = 0;
while (running) {
if (bf.isEmpty()) {
if (prod != null) {
synchronized (prod) {
prod.notify();
}
}
myWait(0);
} else {
sum += bf.remove(0);
counter++;
}
}
System.out.println("for first " + counter + " nums an avg = " + ((double) sum / counter));
}
private void myWait(long millisecs) {
System.out.println("consumer is waiting.");
try {
synchronized (this) {
this.wait(millisecs);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("consumer is NOT waiting.");
}
public void terminate() {
this.running = false;
}
}
Producer class:
package producer.consumer2;
public class Producer implements Runnable {
private final Buffer<Integer> bf;
private volatile boolean running = true;
private Consumer cons;
public Producer(Buffer<Integer> bf) {
this.bf = bf;
}
public void setConsumer(Consumer cons) {
this.cons = cons;
}
#Override
public void run() {
int counter = 1;
while (running) {
if (bf.isFull()) {
if (cons != null) {
synchronized (cons) {
cons.notify();
}
}
myWait(0);
} else {
bf.add(counter);
counter++;
}
}
}
private void myWait(long millisecs) {
System.out.println("producer is waiting.");
try {
synchronized (this) {
this.wait(millisecs);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("producer is NOT waiting.");
}
public void terminate() {
this.running = false;
}
}
Looks like a regular case of 'missed signal'. Since both consumer and producer just wait without checking a condition, yu have no way to ensure the notify actually happens during the waiting.
e.g. in Consumer :
if (prod != null) {
synchronized (prod) {
prod.notify();
}
}
myWait(0);
Note that if, after prod.notify() the Production thread does all of its work, and notifies the consumer, before it even starts waiting, the consumer will start waiting for a signal that's already been given, and missed.
Always take into account that waiting may not be needed anymore. So always check a condition before even starting to wait. In your case here, the consumer should not even begin waiting if the buffer is full. And likewise the producer should not start waiting if the buffer is empty.
It's also possible to get spurious wake ups. So you'll have to re-check the condition when returning from waiting. The typical idiom is this :
synchronized(monitor) {
while (!stateBasedCondition) {
monitor.wait();
}
}
import java.awt.List;
import java.util.ArrayList;
import java.util.Random;enter code here
public class shared {
private int [] buffer;
private int capacity;
Object lock;//=new Object();
int count=0;
int i,j;
//int i=0,j=0;
public shared(int capacity){
this.capacity = capacity;
buffer = new int[capacity];
i=0;
j=0;
lock = new Object();
//for(int k=0;k<capacity;k++){
//buffer[k]=0;
//}
}
public void producer() throws InterruptedException {
//synchronized(lock){
while(true){
synchronized(lock){
Random rn = new Random();
int number = rn.nextInt(100);
Thread.sleep(1000);
while(count==capacity){
try{
System.out.println("BUffer is full");
lock.wait();
}catch(InterruptedException e){
}
//System.out.println("buffer is full");
}
System.out.println("producing" + number);
buffer[i] = number;
i = (i+1)%capacity;
count++;
lock.notifyAll();
}
}
}
public void consumer(){
//synchronized(lock){
while(true){
synchronized(lock){
while(count==0){
try{
lock.wait();
}catch(InterruptedException e){
}
}
//int a = buffer.get(0);
//buffer.remove(0);
int consumed = buffer[j];
System.out.println("consuming" + consumed);
j=(j+1)%capacity;
count--;
lock.notifyAll();
if((consumed%2)==0){
System.out.println("the number displayed by the consumer is " + consumed);
}
}
}
}
}
public class producer implements Runnable {
shared h;
public producer(shared s) {
// TODO Auto-generated constructor stub
h=s;
}
public void run(){
//for(int i=0;i<10;i++){
try {
h.producer();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class consumer implements Runnable {
shared h;
public consumer(shared s) {
// TODO Auto-generated constructor stub
h=s;
}
public void run(){
//for(int i=0;i<5;i++){
h.consumer();
}
}
public class implement {
public static void main(String [] args) throws InterruptedException{
shared s = new shared(10);
Thread pro1 = new Thread(new producer(s));
Thread con1 = new Thread(new consumer(s));
pro1.start();
con1.start();
pro1.join();
con1.join();
}
}
This is the entire code that I have written. The problem that I am getting is that my buffer is getting entirely full first then it is getting consumed. I want it to be consumed randomly i.e. when the first element fills up it might get get consumed or after 3-4 elements filled up.
I've reproduced your experiment and checked that, in fact, the producer thread fills the buffer before the consumer comes in. And that's because only when the buffer is full, the producer calls wait to give the consumers a chance.
The behaviour in a multi-threaded environment is always something random. Surely when the execution exits from the producer's synchronized block, the next iteration comes so quickly that it enters again in the block before other threads have a chance to.
If you want to produce and consume with an random ratio, I suggest you other means:
First, move the sleep out of the synchronized block.
Or else, do always a wait first-notify last into your producer loop, but performing a random number of inserts between them.
I’m writing a program that implements the Producer Consumer problem in Java using multithreading concepts. Below are few details how I’m supposed to do it:
1) The main thread should create a buffer with capacity specified as a command line argument. The number of producer and consumer threads are also specified as command line arguments. I’m supposed to assign a unique number to each producer and consumer thread. How do I assign a unique number to producer and consumer threads?
2) The producer thread operates in an infinite loop. It produces a data item (a string) with the following format: <producer number>_<data item number>. For example the 1st data item from thread number 1 will be 1_1 and second data item from thread number 3 will be 3_2. How do create data items in such a format?
3) Then the Producer thread writes an entry into the producer log file (< producer number > “Generated” <data item>). Upon writing the log entry, it attempts to insert into the buffer. If insertion is successful, it creates an entry into the log file (<producer number> <data item> “Insertion successful”). How do I write such a code?
Below is the Java code I wrote.
import java.util.*;
import java.util.logging.*;
public class PC2
{
public static void main(String args[])
{
ArrayList<Integer> queue = new ArrayList<Integer>();
int size = Integer.parseInt(args[2]);
Thread[] prod = new Thread[Integer.parseInt(args[0])];
Thread[] cons = new Thread[Integer.parseInt(args[1])];
for(int i=0; i<prod.length; i++)
{
prod[i] = new Thread(new Producer(queue, size));
prod[i].start();
}
for(int i=0; i<cons.length; i++)
{
cons[i] = new Thread(new Consumer(queue, size));
cons[i].start();
}
}
}
class Producer extends Thread
{
private final ArrayList<Integer> queue;
private final int size;
public Producer(ArrayList<Integer> queue, int size)
{
this.queue = queue;
this.size = size;
}
public void run()
{
while(true){
for(int i=0; i<size; i++)
{
System.out.println("Produced: "+i+" by id " +Thread.currentThread().getId());
try
{
produce(i);
Thread.sleep(3000);
}
catch(Exception e)
{
Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, e);
}
}}
}
public void produce(int i) throws InterruptedException
{
while(queue.size() == size)
{
synchronized(queue)
{
System.out.println("Queue is full "+Thread.currentThread().getName() +" is waiting, size: "+queue.size());
queue.wait();
}
}
synchronized(queue)
{
queue.add(i);
queue.notifyAll();
}
}
}
class Consumer extends Thread
{
private final ArrayList<Integer> queue;
private final int size;
public Consumer(ArrayList<Integer> queue, int size)
{
this.queue = queue;
this.size = size;
}
public void run()
{
while(true)
{
try
{ System.out.println("Consumed: "+consume());
Thread.sleep(1000);
}
catch(Exception e)
{
Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, e);
}
}
}
public int consume() throws InterruptedException
{
while(queue.isEmpty())
{
synchronized(queue)
{
System.out.println("Queue is empty "+Thread.currentThread().getName()+" is waiting, size: "+queue.size());
queue.wait();
}
}
synchronized (queue)
{
queue.notifyAll();
System.out.println("Consumed by id "+Thread.currentThread().getId());
return (Integer) queue.remove(0);
}
}
}
How can I carry out the above steps?
I’m supposed to assign a unique number to each producer and consumer
thread. How do I assign a unique number to producer and consumer
threads?
Add an instance (non-static) variable to the Producer/Consumer classes. When you initialize the new Producer/Consumer Objects, pass in the unique number. You can keep track of what number you're on with an int counter in your main class.
2) The producer thread operates in an infinite loop. It produces a
data item (a string) with the following format: < producer number >_<
data item number > . For example the 1st data item from thread number
1 will be 1_1 and second data item from thread number 3 will be 3_2.
How do create data items in such a format?
Use synchronized methods and/or atomic variables. Look into Java Concurrency.
3) Then the Producer thread writes an entry into the producer log file
(< producer number > “Generated” < data item >). Upon writing the log
entry, it attempts to insert into the buffer. If insertion is
successful, it creates an entry into the log file (< producer number >
< data item > “Insertion successful”). How do I write such a code?
My answer is the same as the previous question: read about Java concurrency. Spend an hour reading about synchronization, locks, and atomic variables and I guarantee you will easily write your program.
For producer consumer problem best solution is BlockingQueue. I was testing a few things so designed same kind of program now modified it as per your need.
See if it helps.
import java.util.concurrent.*;
public class ThreadingExample {
public static void main(String args[]){
BlockingQueue<Message> blockingQueue = new ArrayBlockingQueue<Message>(100);
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Producer(blockingQueue));
exec.execute(new Consumer(blockingQueue));
}
}
class Message{
private static int count=0;
int messageId;
Message(){
this.messageId=count++;
System.out.print("message Id"+messageId+" Created ");
}
}
class Producer implements Runnable{
private BlockingQueue<Message> blockingQueue;
Producer(BlockingQueue<Message> blockingQueue){
this.blockingQueue=blockingQueue;
}
#Override
public void run(){
while(!Thread.interrupted()){
System.out.print("Producer Started");
try {
blockingQueue.put(new Message());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Producer Done");
}
}
}
class Consumer implements Runnable{
private BlockingQueue<Message> blockingQueue;
Consumer(BlockingQueue<Message> blockingQueue){
this.blockingQueue=blockingQueue;
}
#Override
public void run(){
while(!Thread.interrupted()){
System.out.print("Concumer Started");
try{
Message message = blockingQueue.take();
System.out.print("message Id"+message.messageId+" Consumed ");
}
catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Concumer Done");
}
}
}
I tried the following which might work for you, except for the buffer condition on 3, which you can add the part of the code by yourself.
Hope this helps.
public class Message {
private String msg;
public Message(String msg) {
super();
this.msg = msg;
}
public String getMsg(){
return msg;
}
}
import java.util.concurrent.BlockingQueue;
public class Producer implements Runnable {
private BlockingQueue<Message> queue;
private boolean run = true;
public Producer(BlockingQueue<Message> queue) {
super();
this.queue = queue;
}
public void setRun(boolean val) {
this.run = val;
}
#Override
public void run() {
int i = 0;
while (run) {
Message msg = new Message(Thread.currentThread().getName() + "_"+ i);
try {
Thread.sleep(i * 100);
queue.put(msg);
System.out.println("Producer: "+Thread.currentThread().getName()+" produced and added to the queue: "+msg.getMsg());
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
if(i==10){
setRun(false);
System.out.println(Thread.currentThread().getName()+" stopped");
}
}
}
}
import java.util.concurrent.BlockingQueue;
public class Consumer implements Runnable{
private BlockingQueue<Message> queue;
private boolean run = true;
public Consumer(BlockingQueue<Message> queue) {
super();
this.queue = queue;
}
public void setRun(boolean val){
this.run = val;
}
#Override
public void run() {
while(run){
try {
Thread.sleep(100);
Message msg = queue.take();
System.out.println("Consumer: "+Thread.currentThread().getName()+" generated/consumed "+msg.getMsg());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
import java.util.Scanner;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class ProducerConsumerMain {
public static void main(String[] args) {
System.out
.println("please enter the number of producer:consumer:size of the queue in order");
Scanner scan = new Scanner(System.in);
Thread[] prodThreads = new Thread[scan.nextInt()];
Thread[] consThreads = new Thread[scan.nextInt()];
BlockingQueue<Message> queue = new ArrayBlockingQueue<Message>(scan.nextInt());
for (int i = 0; i < prodThreads.length; i++) {
prodThreads[i] = new Thread(new Producer(queue), "" + i);
prodThreads[i].start();
}
for (int i = 0; i < consThreads.length; i++) {
consThreads[i] = new Thread(new Consumer(queue), "" + i);
consThreads[i].start();
}
}
}
Please refer the below code. You can change the constant values based on the command line arguments. I have tested the code, its working as per your requirement.
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerProblem {
public static int CAPACITY = 10; // At a time maximum of 10 tasks can be
// produced.
public static int PRODUCERS = 2;
public static int CONSUMERS = 4;
public static void main(String args[]) {
Queue<String> mTasks = new LinkedList<String>();
for (int i = 1; i <= PRODUCERS; i++) {
Thread producer = new Thread(new Producer(mTasks));
producer.setName("Producer " + i);
producer.start();
}
for (int i = 1; i <= CONSUMERS; i++) {
Thread consumer = new Thread(new Consumer(mTasks));
consumer.setName("Consumer " + i);
consumer.start();
}
}
}
class Producer implements Runnable {
Queue<String> mSharedTasks;
int taskCount = 1;
public Producer(Queue<String> mSharedTasks) {
super();
this.mSharedTasks = mSharedTasks;
}
#Override
public void run() {
while (true) {
synchronized (mSharedTasks) {
try {
if (mSharedTasks.size() == ProducerConsumerProblem.CAPACITY) {
System.out.println("Producer Waiting!!");
mSharedTasks.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
while (mSharedTasks.size() != ProducerConsumerProblem.CAPACITY) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
String produceHere = Thread.currentThread().getName()
+ "_Item number_" + taskCount++;
synchronized (mSharedTasks) {
mSharedTasks.add(produceHere);
System.out.println(produceHere);
if (mSharedTasks.size() == 1) {
mSharedTasks.notifyAll(); // Informs consumer that there
// is something to consume.
}
}
}
}
}
}
class Consumer implements Runnable {
Queue<String> mSharedTasks;
public Consumer(Queue<String> mSharedTasks) {
super();
this.mSharedTasks = mSharedTasks;
}
#Override
public void run() {
while (true) {
synchronized (mSharedTasks) {
if (mSharedTasks.isEmpty()) { // Checks whether there is no task
// to consume.
try {
mSharedTasks.wait(); // Waits for producer to produce!
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
while (!mSharedTasks.isEmpty()) { // Consumes till task list is
// empty
try {
// Consumer consumes late hence producer has to wait...!
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (mSharedTasks) {
System.out.println(Thread.currentThread().getName()
+ " consumed " + mSharedTasks.poll());
if (mSharedTasks.size() == ProducerConsumerProblem.CAPACITY - 1)
mSharedTasks.notifyAll();
}
}
}
}
}
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
p1.start();
c1.start();
}
}
class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e) {
}
}
available = false;
notifyAll();
return contents;
}
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e) {
}
}
contents = value;
available = true;
notifyAll();
}
}
class Consumer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Consumer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumer #"
+ this.number
+ " got: " + value);
}
}
}
class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" + this.number
+ " put: " + i);
try {
sleep((int) (Math.random() * 100));
} catch (InterruptedException e) {
}
}
}
}
I implemented a buffer for the producer/consumer pattern, however, it seems that the Consumer never acquires the lock so Starvation occurs. I can't identify why this happens since both put() and take() seem to release the lock properly...
I know there is BlockingQueue and other nice implementations, but I want to implement this using wait() and notify() as an exercise.
public class ProducerConsumerRaw {
public static void main(String[] args) {
IntBuffer buffer = new IntBuffer(8);
ConsumerRaw consumer = new ConsumerRaw(buffer);
ProducerRaw producer = new ProducerRaw(buffer);
Thread t1 = new Thread(consumer);
Thread t2 = new Thread(producer);
t1.start();
t2.start();
}
}
class ConsumerRaw implements Runnable{
private final IntBuffer buffer;
public ConsumerRaw(IntBuffer b){
buffer = b;
}
public void run() {
while(!buffer.isEmpty()) {
int i = buffer.take();
System.out.println("Consumer reads "+i); // this print may not be in the order
}
}
}
class ProducerRaw implements Runnable{
private final IntBuffer buffer;
ProducerRaw(IntBuffer b) {
this.buffer = b;
}
public void run(){
for (int i = 0; i < 20; i++) {
int n = (int) (Math.random()*100);
buffer.put(n);
System.out.println("Producer puts "+n);
}
}
}
class IntBuffer{
private final int[] storage;
private volatile int end;
private volatile int start;
public IntBuffer(int size) {
this.storage = new int[size];
end = 0;
start = 0;
}
public void put(int n) { // puts add the END
synchronized(storage) {
boolean full = (start == (end+storage.length+1)%storage.length);
while(full){ // queue is full
try {
storage.notifyAll();
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.storage[end] = n;
end = incrementMod(end);
storage.notifyAll();
}
}
public int take(){
synchronized(storage) {
while (end == start) { // empty queue
try {
storage.notifyAll(); // notify waiting producers
storage.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int index = start;
start = incrementMod(start);
storage.notifyAll(); // notify waiting producers
return this.storage[index];
}
}
private int incrementMod(int index) {
synchronized (storage) {
if (index == storage.length-1) return 0;
else return index+1;
}
}
public boolean isEmpty(){
synchronized (storage) {
return (start == end);
}
}
}
This is at least one problem, in your put method:
boolean full = (start == (end+storage.length+1)%storage.length);
while(full){ // queue is full
// Code that doesn't change full
}
If full is ever initialized as true, how do you expect the loop to end?
The other problem is this loop, in the consumer:
while(!buffer.isEmpty()) {
int i = buffer.take();
System.out.println("Consumer reads "+i);
}
You're assuming the producer never lets the buffer get empty - if the consumer starts before the producer, it will stop immediately.
Instead, you want some way of telling the buffer that you've stopped producing. The consumer should keep taking until the queue is empty and won't receive any more data.