I'm learning about threads and Producer-Consumer model .
I haved copy-paste the code from a book and i have a issue.
When I run the program sometimes the Consumer gets the next number from the buffer even if the producer hasn't put nothing in the buffer.
Example :

Producatorul a pus: 0
Consumatorul a primit:  0
Producatorul a pus: 1
Consumatorul a primit:  1
Producatorul a pus: 2
Consumatorul a primit:  2
Producatorul a pus: 3
Consumatorul a primit:  3
Producatorul a pus: 4
Consumatorul a primit:  4
Producatorul a pus: 5
Consumatorul a primit:  5
Producatorul a pus: 6
Consumatorul a primit:  6
Consumatorul a primit:  7 -Here from where the consumer gets the number ???
Producatorul a pus: 7    -Here is where the producer puts the number,after the consumer has got it already
Producatorul a pus: 8
Consumatorul a primit:  8
Producatorul a pus: 9
Consumatorul a primit:  9

Here are the classes :

Consumer:

class Consumator extends Thread {
    private Buffer buffer;
        private String mesaj;
    public Consumator(Buffer b) {
        buffer = b;
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            mesaj = buffer.get();
            System.out.println("Consumatorul a primit:\t" + mesaj);
        }


    }
}

Producer:

class Producator extends Thread {
    private Buffer buffer;
        private String mesaj;
    public Producator(Buffer b) {
        buffer = b;
    }
    public void run() {
        for (int i = 0; i < 10; i++) {
            buffer.put(""+i);
            System.out.println("Producatorul a pus:\t" + i);
            try {
                sleep((int)(Math.random() * 100));
            } catch (InterruptedException e) { }
        }
    }
}

Buffer:

class Buffer {
    private String mesaj="";
    private boolean available = false;
    public synchronized String get() {
        while (!available) {
            try {
                wait();
                //asteapta producatorul sa puna o valoare
            } catch (InterruptedException e) { }
        }
        available = false;
        notifyAll();
        return mesaj;
    }
    public synchronized void put(String number) {
        while (available) {
            try {
                wait();
                //asteapta consumatorul sa preia valoarea
            } catch (InterruptedException e) { }
            }
        this.mesaj = number;
        available = true;
        notifyAll();
    }
}

TestClas:

public class TestSincronizare1 {
    public static void main(String[] args) {
        Buffer b = new Buffer();
        Producator p1 = new Producator(b);
        Consumator c1 = new Consumator(b);
        p1.start();
        c1.start();
    }
}

Edited 3 Years Ago by dinamit3

You need to remember that the synchronized keyword is the thing that protects you from having two threads running at the same time. As long as you are in a synchronized block there is only one thread, but as soon as you leave that block the threads may both start doing things simultaneously.

When your producer puts a value into the buffer it notifies all waiting threads. Suppose that your consumer was waiting and now wakes up. Your producer immediately leaves its synchronized block, which gives the consumer a chance to run because it needs to synchronize to proceed, since its wait is inside a synchronized block. Now both threads can run at the same time.

The next step for the producer thread is to print out a message to report the value it put in the buffer, and it seems you expect that to happen first, but that is only one option for what might happen. The next step for the consumer is set available = false, notify anyone who is waiting (no one, in this case), and then print the value that was stored. Clearly the consumer has to work fast to get to the print before the producer gets to its print, but in any good race the winner is only decided at the finish line. Don't doubt that this is a race; as soon as the producer's synchronized block ends, the starting gun fires and both threads are running.

If you want to control the threads more tightly and avoid this race, then you could do it like this:

synchronized(buffer) {
    mesaj = buffer.get();
    System.out.println("Consumatorul a primit:\t" + mesaj);
}

This expands the synchronized block of get to include the print statement. If you also do this for the put, then there will be no chance for the other thread to sneak anything in between the put and the print that follows it.

This article has been dead for over six months. Start a new discussion instead.