0

I have written a simple java multithreading program.

I am having some questions regarding the code. Please help me with these questions.

Thanks in advance!

Here is my code:

Producer.java

package com.prodcon;
import java.util.Stack;
public class Producer extends Thread {
    DataStorage data;
    MainProcess tempmp;
    public Producer(DataStorage dst, MainProcess mp){
        data = dst;
        tempmp = mp;
    }
    public void run(){
        for(int i = 0; i < 3; i++){
            System.out.println("Thread:"+this.getName()+"called");
            data.PutData();
            /*-------------current states---------------------*/
            System.out.println("Current states of the threads:");
            System.out.println("p1->"+tempmp.p1.getState());
            System.out.println("p2->"+tempmp.p2.getState());
            System.out.println("p3->"+tempmp.p3.getState());
            System.out.println("c1->"+tempmp.c1.getState());
            System.out.println("c2->"+tempmp.c2.getState());
            System.out.println("c3->"+tempmp.c3.getState());
            /*-------------current states---------------------*/

        }
    }
}

consumer.java

  package com.prodcon;

    public class Consumer extends Thread {
        DataStorage data;
        MainProcess tempmp;
        public Consumer(DataStorage dst, MainProcess mp){
            data = dst;
            tempmp = mp;
        }
        public void run(){
            for(int i = 0; i < 3; i++){
                System.out.println("Thread:"+this.getName()+"called");
                data.GetData();
                /*-------------current states---------------------*/
                System.out.println("Current states of the threads:");
                System.out.println("p1->"+tempmp.p1.getState());
                System.out.println("p2->"+tempmp.p2.getState());
                System.out.println("p3->"+tempmp.p3.getState());
                System.out.println("c1->"+tempmp.c1.getState());
                System.out.println("c2->"+tempmp.c2.getState());
                System.out.println("c3->"+tempmp.c3.getState());
                /*-------------current states---------------------*/
            }
        }
    }

DataStorage.java

    package com.prodcon;

import java.util.Random;
import java.util.Stack;

import javax.xml.crypto.Data;

public class DataStorage {

    int countofdata;
    Stack<Double> data;

    public DataStorage() {
        countofdata = 0;
        data = new Stack<Double>();
    }

    public synchronized void GetData() {
        while (data.isEmpty()) {
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
            }

        }
        double temp = (double) data.pop();
        //System.out.println("Data poped out:" + temp);
        countofdata++;
        notifyAll();
    }

    public synchronized void PutData() {
        while (true) {
            if (data.size() == 3) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } else {
                break;
            }
        }
        double temp = Math.random();
        data.push(temp);
        //System.out.println("Data inserted in storage:" + temp);
        countofdata--;
        notifyAll();
    }
}

MainProcess.java

    package com.prodcon;

public class MainProcess {

    /**
     * @param args
     */
    DataStorage ProcessData;
    public Producer p1, p2, p3, p4;
    public Consumer c1, c2, c3, c4;
    public MainProcess(){
        ProcessData = new DataStorage();
        p1 = new Producer(ProcessData, this);
        p2 = new Producer(ProcessData, this);
        p3 = new Producer(ProcessData, this);
        c1 = new Consumer(ProcessData, this);
        c2 = new Consumer(ProcessData, this);
        c3 = new Consumer(ProcessData, this);
        p1.setName("p1");
        p2.setName("p2");
        p3.setName("p3");
        c1.setName("c1");
        c2.setName("c2");
        c3.setName("c3");
    }
    public void startprocess(){
        p1.start();
        p2.start();
        p3.start();
        c1.start();
        c2.start();
        c3.start();
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        MainProcess mp1 = new MainProcess();
        mp1.startprocess();

    }

}

And heres is the output of this program:

    Thread:p2called
Thread:p3called
Current states of the threads:
Thread:p1called
Current states of the threads:
Thread:c3called
Current states of the threads:
p1->RUNNABLE
p2->BLOCKED
p3->RUNNABLE
c1->BLOCKED
c2->BLOCKED
c3->BLOCKED
Thread:p3called
Current states of the threads:
p1->RUNNABLE
p2->BLOCKED
p3->RUNNABLE
c1->BLOCKED
c2->BLOCKED
c3->BLOCKED
Thread:p3called
Thread:c2called
Thread:c1called
Current states of the threads:
Current states of the threads:
p1->RUNNABLE
Current states of the threads:
p2->BLOCKED
p1->RUNNABLE
p2->BLOCKED
p3->BLOCKED
c1->BLOCKED
c2->BLOCKED
c3->BLOCKED
Thread:p1called
Current states of the threads:
p1->RUNNABLE
p2->BLOCKED
p3->BLOCKED
c1->BLOCKED
c2->BLOCKED
c3->BLOCKED
Thread:p1called
Current states of the threads:
p1->RUNNABLE
p2->BLOCKED
p3->BLOCKED
c1->RUNNABLE
c2->RUNNABLE
c3->BLOCKED
Thread:c1called
Current states of the threads:
p1->BLOCKED
p2->BLOCKED
p3->BLOCKED
c1->RUNNABLE
c2->RUNNABLE
c3->BLOCKED
Thread:c1called
Current states of the threads:
p1->BLOCKED
p2->BLOCKED
p3->BLOCKED
c1->RUNNABLE
c2->RUNNABLE
c3->BLOCKED
Current states of the threads:
p1->RUNNABLE
p2->BLOCKED
p3->BLOCKED
c1->TERMINATED
c2->RUNNABLE
c3->BLOCKED
p1->RUNNABLE
p2->BLOCKED
p3->BLOCKED
c1->TERMINATED
c2->BLOCKED
c3->RUNNABLE
Thread:c3called
Current states of the threads:
p1->TERMINATED
p2->BLOCKED
p3->BLOCKED
c1->TERMINATED
c2->BLOCKED
c3->RUNNABLE
Thread:c3called
Current states of the threads:
p1->TERMINATED
p2->BLOCKED
p3->BLOCKED
c1->TERMINATED
c2->BLOCKED
c3->RUNNABLE
p3->RUNNABLE
p1->BLOCKED
p2->RUNNABLE
p3->RUNNABLE
c1->TERMINATED
c2->BLOCKED
c3->TERMINATED
Thread:p2called
Current states of the threads:
p1->TERMINATED
p2->RUNNABLE
p3->RUNNABLE
c1->TERMINATED
c2->BLOCKED
c3->TERMINATED
Thread:p2called
Current states of the threads:
p1->TERMINATED
p2->RUNNABLE
p3->RUNNABLE
c1->TERMINATED
c2->BLOCKED
c3->TERMINATED
p1->TERMINATED
p2->TERMINATED
p3->RUNNABLE
c1->TERMINATED
c2->RUNNABLE
c3->TERMINATED
Thread:c2called
Current states of the threads:
p1->TERMINATED
p2->TERMINATED
p3->RUNNABLE
c1->TERMINATED
c2->RUNNABLE
c3->TERMINATED
Thread:c2called
Current states of the threads:
p1->TERMINATED
p2->TERMINATED
p3->RUNNABLE
c1->TERMINATED
c2->RUNNABLE
c3->TERMINATED
c1->TERMINATED
c2->TERMINATED
c3->TERMINATED

My questions are :

1.According to the program this process should never stop...but then some threads are automatically getting terminated why??

2.Even after asking some threads to go in wait state ..No thread is going in wait state according to the output. why and how?

3.According to this code : What is the difference between the producer-consumer problem and reader-writer problem?

Thanks again!!

Ravindra babu
  • 37,698
  • 11
  • 250
  • 211
Nullpointer
  • 1,086
  • 7
  • 20
  • Just a hint try to debug with a lots of sysout lines and you will find when and where is the control leaving the thread. – Narendra Pathai Oct 19 '13 at 05:30
  • But even these sysout lines are not getting printed properly..You can see from the code and the actual output ..Order of output and sysout lines is very much different – Nullpointer Oct 19 '13 at 07:39
  • Producer-consumer is the same as reader-writer. The difference might be that producer-consumer implies a FIFO of items (producer waits until that the produced items are consumed) whereas reader-writer focuses on locking a random-accessed areas of a shared resource. This means that arbitrary number of readers or none may read one resource one after another or at the same time. However, this is not possible in producer-consumer, which implies that `read = resource consumed` and, thus, read exactly once. – Val Oct 19 '13 at 10:00

2 Answers2

1

First&second questions - this code naturally exits because of the logic. Try adding a debug output here to see when producers quit.

if (data.size() == 3) {
    try {
        wait();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
} else {
    System.out.printf("%s: breaking as data size is %d, %d%n", Thread.currentThread().getName(), countofdata, data.size());

    break;
}

Another possible problem is:

for(int i = 0; i < 3; i++){
    data.PutData();
}

You put only three times into the queue.

Hint: classy implementation of the producer-consumer problem in Java is i.e. LinkedBlockingQueue.

Third question. I'll try to explain this with producers and consumers. Reader-writer problem is when you don't have producers, but have many consumers which access a single shared resource. Some of these can read the resource and some can write to the resource. Simultaneous reads are possible and the write lock is exclusive.

UPDATE

To emulate multiple reads, you may try using

  • LinkedBlockingQueue is a well-perfomant equivalent for your DataStorage.
  • ReentrantReadWriteLock
  • Or update your solution. This is study example and I didn't test, but should give you the general idea:
final Object readLock = new Object();      //use objects as locks
final Object writeLock = new Object();
public void GetData() {
    synchronized (readLock) {             // acquire only the read lock
        while (data.isEmpty()) {
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
            }

        }
        double temp = (double) data.pop();
        //System.out.println("Data poped out:" + temp);
        countofdata++;
        System.out.printf("%s: %d, %d%n", Thread.currentThread().getName(), countofdata, data.size());
        notifyAll();        
     }
 }

public void PutData() {
    synchronized (readLock) {       //first, acquire the read lock
        synchronized (writeLock) {  // then acquire the write lock
            while (true) {
                if (data.size() == 3) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } else {
                    System.out.printf("%s: breaking as data size is %d, %d%n", Thread.currentThread().getName(), countofdata, data.size());

                    break;
                }
            }
            double temp = Math.random();
            data.push(temp);
            //System.out.println("Data inserted in storage:" + temp);
            countofdata--;
            notifyAll();
        }
    }
}
Andrey Chaschev
  • 16,160
  • 5
  • 51
  • 68
  • Still I am confused about the answer of first two questions..When producer finds that data storage is full it should wait but here it is getting terminated! why? And Thanks for the answer of third question I got the concept :)..but then if I am using "synchronized" keyword in my program then how can I allow multiple reads at a time? – Nullpointer Oct 19 '13 at 07:45
  • 'synchronized' for the method means exclusive access on the `this` object. This is a very raw synch method and it is a bad smell when you have many of them for CPU-intensive parts of app which your queue is. So you won't be able to multiple read on a single data source from different threads. To do this you could... I'll update the answer. – Andrey Chaschev Oct 19 '13 at 07:57
  • Well,Thanks for answer ...If I am asking for the readLock in every GetData() call then also only one reader can read the data i.e. the one who gets the readLock, write? How can multiple readers read the data? And PutData() part is working according to the algorithm :) Thanks again! – Nullpointer Oct 19 '13 at 08:26
  • What I think is in GetData method instead of acquiring readLock we should just acquire writeLock so that all writers are blocked but reader will not be blocked!..I don't know whether this is right or wrong? this is just my view..sorry if I am posting any wrong ideas! :p – Nullpointer Oct 19 '13 at 08:36
  • "GetData() call then also only one reader can read the data i.e. the one who gets the readLock" - yes. `ReadWriteLock` doesn't have this problem. 'What I think is in GetData method instead of acquiring readLock we should just acquire writeLock so that all writers are blocked but reader will not be blocked!' - it has just the same problem and is like your initial solution. `ReadWriteLock` is quiet a non-trivial task and you may want to study `ReentrantReadWriteLock` implementation. – Andrey Chaschev Oct 19 '13 at 09:32
  • After a lot of surfing I am finally able to complete my Readers writers problem! I was able to achieve simultaneous reading of two or more readers Thanks to Andrey for his suggestion(ReentrantReadWriteLock) ..But I still have one doubt :p.. when I run my code again my writer thread gets terminated after some time! (in a same way as described for my producer consumer problem.. why? Thanks a lot to all of you guys for helping me!! – Nullpointer Oct 19 '13 at 10:44
  • You're welcome! Just a guess - may be you need an infinite producer for threads not to stop? – Andrey Chaschev Oct 19 '13 at 11:06
0

I can see your very keen on using threads and that this is the topic of the question but have you considered Observer/Observable?

The Datastorage is your Observable.

The consumers are the Observers. They're looking for change.

I modified your code as such and only have one thread that runs periodically.

Producer:

 public class Producer{
    DataStorage data;
    MainProcess tempmp;

    public Producer(DataStorage dst, MainProcess mp){
        data = dst;
        tempmp = mp;
    }
    public void changeTheData(){

            data.PutData();
            System.out.println("INFO :: Producer :: Just put data");
    }
}

Consumer:

 import java.util.Observable;
 import java.util.Observer;
 import java.util.Stack;
 import java.util.concurrent.atomic.AtomicReference;

 public class Consumer implements Observer {
    DataStorage data;
    MainProcess tempmp;
    public Consumer(DataStorage dst, MainProcess mp){
        data = dst;
        tempmp = mp;
    }


 public void update(Observable arg0, Object arg1) {
    System.out.println("INFO :: class is " + arg1.getClass().toString());
    System.out.println("INFO :: Consumer :: data is " + ((AtomicReference<Double>)arg1).get());


}
}

DataStorage:

 import java.util.Observable;
 import java.util.concurrent.atomic.AtomicReference;

 public class DataStorage extends Observable {

   int countofdata;
   AtomicReference<Double> data = new AtomicReference<Double>();

   public DataStorage() {
       countofdata = 0;

   }

   public AtomicReference<Double> GetData() {

       // System.out.println("Data poped out:" + temp);
       countofdata--;
       return data;

   }

   public synchronized void PutData() {

      this.data.set(Math.random());
      System.out.println("INFO :: DataStorage :: Data inserted in storage:" + this.data.get());
      this.setChanged();
      this.notifyObservers(data);
      countofdata++;

  }
 }

MainProcess:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MainProcess implements Runnable{

/**
 * @param args
 */
DataStorage processData;
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);
private Producer p1, p2, p3, p4;
private Consumer c1, c2, c3, c4;

public MainProcess(){

    processData = new DataStorage();
    p1 = new Producer(processData, this);
    p2 = new Producer(processData, this);
    p3 = new Producer(processData, this);
    c1 = new Consumer(processData, this);
    c2 = new Consumer(processData, this);
    c3 = new Consumer(processData, this);
    processData.addObserver(c1);
    processData.addObserver(c2);
    processData.addObserver(c3);


}
public void startprocess(){
     this.scheduledExecutorService.scheduleAtFixedRate(this, 5, 10, TimeUnit.SECONDS);

}
public static void main(String[] args) {
    // TODO Auto-generated method stub
    MainProcess mp1 = new MainProcess();
    mp1.startprocess();

}
public void run() {
    System.out.println("INFO :: p1 Changing the data");
    p1.changeTheData();
    System.out.println("INFO :: p2 Changed the data");
    p2.changeTheData();
    System.out.println("INFO :: p3 Changed the data");
    p3.changeTheData();
}

}
Tony
  • 345
  • 1
  • 4
  • 11
  • Thanks tony for new code! I am sure it will be very useful to me..by the way tell me one thing When does your program terminates and how? :) – Nullpointer Oct 19 '13 at 10:45
  • I built and ran it in Eclipse. When its running and I want to stop it, I just click the red button in the console and that stops it. – Tony Oct 19 '13 at 15:27
  • If your outside of Eclipse then try: java -cp MyJar.jar my.cool.class.MainProcess <-- this has your main. To stop it, Ctrl C. – Tony Oct 19 '13 at 15:36
  • yeah! I am also using eclipse but my question was ...My above mentioned program terminates automatically but your program keeps running? isn't it? and How? – Nullpointer Oct 19 '13 at 16:33
  • The reason mine continuously runs is that I used a ScheduledExecutorService which schedules to start after 5 seconds and then to run at a fixed rate every 10 seconds. Its in the MainProcess. Because the mainProcess is Runnable it has a run method. The ScheduledExecturService.scheduleAtFixedRate takes as an argument: 1. The runnable Class, 2. Delay to start 3. How often to run and 4. TimeUnits. The concurrency libraries are very useful. I hope the Obsrever/Observable helped you out. – Tony Oct 19 '13 at 17:12
  • In case you wanted to shut your threads down you could use scheduledExecutorSrevice.shutdown() or shutdownNow() – Tony Oct 19 '13 at 17:15
  • Thanks a lot! Now I got the BIG difference in your code and my code...Thanks a lot...This helped me a lot!! – Nullpointer Oct 19 '13 at 17:51