简体   繁体   中英

Java thread producer and consumer program issue

I am trying the Java thread producer and consumer program. but consumer thread always goes to waiting status .

I unable to debug the issues why consumer thread always goes to waiting status or producer not notify to consumer thread

please help me to fix this . The programs are below.

The communicator class calls the both producer and consumer class

public class Communicator {

   Thread t = null;
    Thread t1 = null;

    public void runThread() {
        Producer p = new Producer();
        Consumer c = new Consumer(p);
        t = new Thread(p);
        t1 = new Thread(c);
        t.start();
        t1.start();
        Thread tr = new Thread() {
            public void run() {
                for (int i = 0; i < 30; i++) {
                    System.out.println("t::::::::::::: " + t.getState());
                    System.out.println("t1::::::::::::: " + t1.getState());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
            }
        };
        tr.start();
    }

    public static void main(String[] args) {
        Communicator c = new Communicator();
        c.runThread();
    }
}

This is producer class which append the data in stringbuffer and notify to consumer class

public class Producer extends Thread {
        public StringBuffer sb;

        public Producer() {
            sb = new StringBuffer();
        }

        public void run() {
            synchronized (sb) {
                try {
                    System.out.println("Bala");
                    sb.append("murugan");
                    sb.notify();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

Below is consumer class code . it wait up to get notifications from producer class.

public class Consumer extends Thread {
    public Producer p;

    public Consumer(Producer p) {
        this.p = p;

    }

    public void run(){
        synchronized (p.sb) {
            try {

                p.sb.wait();

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(p.sb);
        }
    }


}

The Producer is already Terminated, and it already invoked notify() before the Consumer invoke wait() .

Since Producer and Consumer extends Thread , update the Communicator class to this:

public class Communicator {
    public void runThread() {
        final Producer p = new Producer();
        final Consumer c = new Consumer(p);

        p.start();
        c.start();

        Thread tr = new Thread() {
            public void run() {
                for (int i = 0; i < 30; i++) {
                    System.out.println("t::::::::::::: " + p.getState());
                    System.out.println("t1::::::::::::: " + c.getState());
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
            }
        };
        tr.start();
    }

    public static void main(String[] args) {
        Communicator c = new Communicator();
        c.runThread();
    }
}

If the Producer is not yet Terminated [ if (p.getState() != Thread.State.TERMINATED) ], that's the only time Consumer will wait:

public class Consumer extends Thread {
    public Producer p;

    public Consumer(Producer p) {
        this.p = p;

    }

    public void run() {
        synchronized (p.sb) {
            try {

                if (p.getState() != Thread.State.TERMINATED) {
                    p.sb.wait();
                }

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(p.sb);
        }

    }
}

There are few problems with your current code in which the Consumer thread always be in waiting state whereas the producer is already terminated.

Also, your StringBuffer object needs to be volatile so that the producer thread writes will be flushed & available to the other thread.

Along with that, I have modified your Producer and Consumer code to make it more realistic (both run in while loop one producing some data and the other receiving the data) as shown below: (I have also added 1 sec sleep to run the things in slower pace so that you can understand the things better):

Consumer class:

public class Producer extends Thread {
        public volatile StringBuffer sb;

        public Producer() {
            sb = new StringBuffer();
            sb.append("");
        }

        public void run() {
            synchronized (sb) {
                try {
                    while(true) {
                        Thread.sleep(1000);
                        if(sb.toString().equals("")) {
                            sb.append("murugan");
                            System.out.println(" producing sb completed *** ");
                            sb.notify();
                        } else {
                            sb.wait();
                        }
                    } 
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

Consumer class:

public class Consumer extends Thread {
        public Producer p;

        public Consumer(Producer p) {
            this.p = p;

        }

        public void run(){
            synchronized (p.sb) {
                try {
                    while(true) {
                        Thread.sleep(1000);
                        if(p.sb.toString().equals("")) {
                          p.sb.wait();
                        } else {
                            String str = p.sb.toString();
                            System.out.println(" consuming sb completed **** "+str);
                            p.sb.replace(0, str.length(), "");
                            p.sb.notify();
                        }
                    }
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(p.sb);
            }
        }
    }

for your question,"I unable to debug the issues why consumer thread always goes to waiting status or producer not notify to consumer thread". Actually your consumer is not always in wait status. You can just put Thread.sleep(1000);before p.sb.wait(); in your Consumer class,then you can see "consumerThread::::::::::::: RUNNABLE" for once. IMHO,your consumer code runs too fast to get wait status,so you miss the runnable status. You can learn more from other answers.

It´s is not a answer but a suggestion ... You could simplify the whole logic using BlockingQueue to transfer data from Producer(s) to Consumer(s). All waits and notifies would disappear!

Producer(s) send data to be consumed calling BlockingQueue.offer(String)

Consumer(s) wait (blocked) for data calling BlockingQueue.pool();

As per your code, Consumer Thread waits for the Producer to notify about the string appended in the StringBuffer .

  1. If Producer thread gets the chance to acquire the lock on shared StringBuffer object ( it enters the synchronized block ) then Consumer Thread will go in Blocked state ( will not be able to enter the synchronized block ) as its also a competitor for the Lock (both compete for acquiring the lock on same shared object).
  2. Producer thread completes its execution, leaves the synchronized block and gets Terminated. Note that the notify code will not have any impact as Consumer thread is not yet waiting on the shared object as it is yet to enter the synchronized block
  3. Consumer thread gets the chance to acquire the lock and enter the synchronized block It waits for some one to give notification on the shared object. But as Producer is already terminated no one gives the notification to Consumer thread and it remains in Waiting state.

Fix : In your case you can simply ensure that Consumer thread is started first and acquires the lock before Producer thread. For this you can have the main thread to sleep for some time after starting the Consumer thread.

t = new Thread(p);
t1 = new Thread(c);
t1.start();
try {
        Thread.sleep(1000);
    }catch (InterruptedException e) {
        e.printStackTrace();
    }
t.start();

Key Point : In case you have only 2 threads, one thread should invoke notify and wait . Other thread upon being notified and only the thread in competition for Lock will acquire the lock and do its job. Upon completion of its job it should invoke notify and will wait for the other thread to do job and give notification once done. This way both the threads will get chance to do their jobs one after the other.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM