0

Below is the code concerning Thread running a producer and a consumer.

public class PC1 {
    public static final int limit = 3;

    public static void main(String[] args) {
        List bag = new ArrayList();
        Producer p = new Producer(bag);
        Consumer c = new Consumer(bag);
        Thread t1 = new Thread(p, "t1");
        Thread t2 = new Thread(c, "t2");
        Thread t3 = new Thread(p, "t3");
        Thread t4 = new Thread(c, "t4");
        Thread t5 = new Thread(p, "t5");
        Thread t6 = new Thread(c, "t6");
        t2.start();
        t4.start();
        t6.start();
        t1.start();
        t3.start();
        t5.start();
    }
}

class Producer implements Runnable {
    private List bag;

    public Producer(List bag) {
        this.bag = bag;
    }

    @Override
    public void run() {
        synchronized (bag) {
            while (true) {
                while (bag.size() >= PC1.limit) {
                    bag.notify();
                    System.out.println(Thread.currentThread().getName() + "@@@@@@@@@@@");
                    try {
                        bag.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int curr = bag.size();

                bag.add(++curr);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " produce " + curr);
            }
        }
    }

}

class Consumer implements Runnable {
    private List bag;

    public Consumer(List bag) {
        this.bag = bag;
    }

    @Override
    public void run() {
        synchronized (bag) {
            while (true) {
                while (bag.size() <= 0) {
                    bag.notify();
                    System.out.println(Thread.currentThread().getName() + "!!!!!!!!!!!");
                    try {
                        bag.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                int curr = bag.size();

                bag.remove(curr - 1);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " consume " + (bag.size() + 1));
            }
        }
    }

}   

t2!!!!!!!!!!!
t3 produce 1
t3 produce 2
t3 produce 3
t3@@@@@@@@@@@
t1@@@@@@@@@@@
t6 consume 3
t6 consume 2
t6 consume 1
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
t6!!!!!!!!!!!
t4!!!!!!!!!!!
.
.
....like this all the time t6 and t4 alternate

above is the result on my console.As you can see:t2,t3,t1 obtain chance to excute at the beginning,then t6 and t4 alternate,other threads would never get chance to excute.
let me interpret its process.
First,t2 gets privilege to consume,bag.size=0,wait
then,t3 gets privilege to produce,after 3 times,bag is full,notify t2 to make it enter competing state,self wait
then,t1 gets privilege to product,as bag is full,notify t3 to make it enter competing state,self wait
then,t6 gets privilege to consume,after 3 times,bag is empty,notify t3 to make it enter competing state,self wait
then,t4 gets privilege to consume,as bag is empty,notify t6 to make it enter competing state,self wait
.
.
.
I m confused about before t4 wait,all other 5 thread are in competing state,however,the result shows only t4 and t6 alternate,other threads would never get chance to excute.Why that happens?

And another question is that if I modify notify to notifyAll,all 6 threads do get chance to excute.In my understanding,if multi threads are all in competing state,they all should have chance to excute.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
wj539h
  • 11
  • 3

3 Answers3

3

Notify is working correctly; i.e. according to how it is specified. The "problem" is that Java does not guarantee fair scheduling for wait / notify / notifyAll. That means that some threads may get more of the work than others do.

In fact, for a normal program, this does not matter: it is not a problem at all. For example, in a normal multi-threaded producer / consumer application, it doesn't matter which consumer thread processes the things produced by the producers. All that matters is that they are processed efficiently. Indeed, there can be performance advantages in the using unfair thread scheduling instead of fair. One reason is that you can reduce the number of thread context switches.


So how would you achieve your goal?

First of all, don't use wait / notify. If you read the javadocs for these methods, you will see that there is no guarantee of fairness.

One way to get fairness would be to use a ReentrantLock instantiated with fair == true; see the javadoc for details.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I have some concerns after reading this post regarding fairness. It fairness really good and don't cause deadlock? http://stackoverflow.com/questions/37136917/fair-locking-in-reeantrantreadwritelock – Ravindra babu May 11 '16 at 05:14
  • @Ravindrababu - The linked Q&A is about Java 5. I seriously hope you are NOT using Java 5 still. – Stephen C May 11 '16 at 07:37
  • Yes. Unfair scheduling. – Stephen C May 11 '16 at 07:39
  • Sorry @Stephen C,I just updated the content of my question.My understanding for that was written.Would you pls see it.Thanks – wj539h May 11 '16 at 08:00
1

If you are going to have more than just one producer and just one consumer, then you must either use notifyAll(); or you must abandon wait()/notify() altogether, switch to a ReentrantLock instead, and use two Conditions.

The problem with using notify() when there are multiple producers or consumers is that it is possible, in some situations, for a producer to wake another producer or, for a consumer to wake another consumer. Once that happens, the cycle is broken, and your program will no longer be able to make any progress. Kind of like a deadlock: Every thread is waiting for some other thread to do something.

The fancy solution to the problem is to have separate Condition variables: One for producers to wake consumers, and one for consumers to wake producers. The blunt-force way to solve the problem is to use notifyAll() and just wake everybody up.

Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • Not entirely correct. If a thread finds itself to be the wrong one to wake up, it may invoke `notify()` again to wake up another thread. This is what is attempted here, as all threads invoke `notify()` in every iteration of the `while` loop. However, there is no guaranty about which thread will wake up, so the two producers could repeatably wake up each other instead of waking up a consumer or a thread could even repeatably wake up itself. – Holger May 10 '16 at 14:32
  • *"Kind of like a deadlock"* - The correct term is "livelock". – Stephen C May 11 '16 at 07:45
1

Stephen's answer is good but fairness is not the only issue, you have so many inaccurate assumptions about how wait works that it would be tiresome to enumerate them all. The wait method is very hard to understand.

James' answer makes a very good point (+1 from me). You can't depend on any particular thread getting notified. Using separate conditions for readers and writers is a good idea to avoid notifyAll.

Big picture, the threads that access your data structure shouldn't be doing the synchronizing, the data structure should be protecting its own state (with synchronization or ReentrantLock or whatever). Reorganize to do that and the program will get a lot easier to write. Also you could read the Oracle tutorial to clear up your misunderstandings about how wait/notify works.

Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276