Producer / Consumer

Hey,

ich bin gerade in das Thema Threads eingestiegen und habe zur Aufgabe ein typisches “Producer / Consumer” Problem bekommen.
Meine Frage wäre, wie ich Informationen zwischen den beiden Threads austauschen kann.

Explizit hier :

Producer erstellt einen Vector(x,y) und der Consumer greift auf den Vector zu, bedient sich diesem und “cleared” ihn dann ( setzt seine Wert auf 0 ).
Wenn ich dem Thread aber versuche im Konstruktor den Vector zu übergeben ist er natürlich immer erst null…

Mein Ansatz:

Producer

import java.util.Random;

public class Producer implements Runnable
{
	int x;
	int y;
	Random r = new Random();
	Vector<Integer> vector;
	
	public Producer()
	{
		
	}
	
	synchronized Vector<Integer> getVector()
	{
		return vector;	
	}
	
	synchronized void createNumbers()
	{

		
		x = r.nextInt(60);
		y = r.nextInt(60);
		
		System.out.println("Thread " + this.toString() + " created random numbers.");
	    
	}	
	
	synchronized void createVector() 
	{
		vector = new Vector<Integer>(x,y);
		System.out.println("Thread " + this.toString() + " created Vector.");
	}
	
	synchronized void clearVector() 
	{
		    vector = new Vector<Integer>(0,0);
			System.out.println("Thread" + this.toString() + " cleared Vector.");
	}
	
	public void run()
	{
		System.out.println("Thread " + this.toString() + " started.");
		
			createNumbers();
			createVector();

			
	}
     
	}
	
public class Consumer implements Runnable {

	private Vector<Integer> item;
	
	public Consumer(Vector<Integer> vector)
	{
		item = vector;
	}
	
	synchronized void createGraphic(Vector<Integer> item2)
	{
		graphic(0,item2);
		graphic(1,item2);		
        System.out.println("Thread " + this.toString() + " created graphic.");
		
	}	
	synchronized void graphic(int x, Vector<Integer> vector)
	{
		for(int i = 0; i <= (Integer)vector.get(x);i++) // *1
		{
			System.out.print("*");			
		}		
		    System.out.print("
");
	}
	
	
	public void run()
	{
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {

		}
		System.out.println("Thread " + this.toString() + " started.");
		createGraphic(item);
	}

}

Ähm ich würde bei dem Consumer nicht im Konstruktor den Vektor übergeben, sondern in der run Methode versuchen diese vom Producer zu holen

Genau das ist ja meine Frage :d
Wie implementier ich sowas ?

Und natürlich soll die Main Methode nur so aussehen


		public static void main(String[] args) throws InterruptedException 
	{
		Producer p = new Producer();
		Consumer c = new Consumer(null);
     	Thread t = new Thread(p);
     	Thread t2 = new Thread(c);
     	
	    t.start();
	    t2.start();
	    t.join();
	    t2.join();

	}


falls nur 1x geholt, dann fast egal ob Konstruktor oder Anfang run-Methode,
muss jedenfalls zu diesem Zeitpunkt irgendwo holbar sein, nicht null,

das ist nur die Frage es richtig zu machen, und eher trivial: erst den Producer erstellen, dann ist der Vector da != null, wie sonst?
Code dazu ist auch keiner gepost, ok, inzwischen, Übergabe null offensichtlich nicht so günstig

aber im Producer ja auch erst noch null, createVector() muss noch erst aufgerufen werden, da ist ja noch viel mehr im Argen,
besser nur einmal am Anfang erstellen, schon gar nicht ständig neuen!, man kann den auch so leeren

der Producer legt auch noch nichts hinein, lauter Baustellen…


bei der Synchronisierung aufpassen, es hilft nicht wenn der Customer und der Producer jeweis auf sich selber synchronisieren,
niemand anders wird da hineinspielen, nichtmal andere Producer usw., da jedes Objekt einzeln

entweder auf das Vector-Objekt, oder auf z.B. den Producer, wenn der Customer den auch komplett kennt,

oder ein drittes Objekt einer eigenen Klasse, dort den Vector drin, darauf synchronisieren

So besser ?

import java.util.Random;
import java.util.Vector;


public class Processor 

{
int x;
int y;
Vector<Integer> vector = new Vector<Integer>();
Object lock = new Object();

	Random r = new Random();
	
	void produce() throws InterruptedException 
	
	{
		while(true)
		{
			synchronized(lock)
			{
		
			x = r.nextInt(60);
			y = r.nextInt(60);
			System.out.println(x);
			System.out.println(y);
			
			System.out.println("Numbers generated");
			vector.add(0,x);
			vector.add(1,y);
			System.out.println("Vector created");
			
			lock.wait();
		
			}
		}
		
		
	}
	
	
	 void consume()
	
			{
		 while(true)
			{
		synchronized(lock)
		{
			for(int i = 0; i <= vector.get(0);i++)
			{
				 System.out.print("*");		
				 	
			}	
			System.out.print("
");
			for(int a = 0; a <= vector.get(1);a++)
			{
				 System.out.print("*");		   
				 
			}	
			System.out.print("
");
			
			vector.clear();
			System.out.println("Vector cleared");
			notifyAll();
		}
			}
		
		
			}
	
}


public class Terminal 
{
	
	public static void main(String[] args) throws InterruptedException
	{
	
	final Processor p = new Processor();
	
	Thread t1 = new Thread (new Runnable()
	{
		public void run()
		{
			try {
				p.produce();
			} catch (InterruptedException e) {
				
			}
		}
	});
	

	
	Thread t2 = new Thread (new Runnable()
	{
		public void run()
		{
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
		
			}
			p.consume();
		}
	});
	
	
	
	t1.start();
	t2.start();
	
	t1.join();
	t2.join();
	
	}
	
	
	
	}

	




FEHLERCODE

32
28
Numbers generated
Vector created
********Exception in thread "Thread-1" *************************
*****************************
Vector cleared
java.lang.IllegalMonitorStateException
	at java.lang.Object.notifyAll(Native Method)
	at Processor.consume(Processor.java:64)
	at Terminal$2.run(Terminal.java:30)
	at java.lang.Thread.run(Unknown Source)

der Fehler ist ja vollkommen klar, notifyAll() darf nur auf etwas aufgerufen werden, an dem auch gerade synchronized-Monitor,
und auch nur daran wartet ja jemand

andersrum wahrscheinlich genauso zu bauen für Wechsel,
und bisschen mehr sleep-Stellen schaden nicht, falls bei Wechsel nicht millionenfach pro Sekunde


dass Consumer davon ausgeht dass immer mindestens bzw. genau zwei Elemente drin sind kann funktionieren bei exakter Abstimmung,
aber einen Vector braucht es dafür eigentlich auch nicht, wieso nicht direkt mit x und y arbeiten?

allgemeiner arbeitet der Consumer die 0-n Elemente nacheinander ab, alles zusammengehörige auch besser als nur ein Element,
der Consumer nimmt die Elemente nacheinander heraus

Jetzt geht es, erreicht aber nach ner Zeit sein limit :o

EDIT:

Ich habe nen Sleep von 1/10 Sekunde eingebaut und jetzt loopt er komplett durch !
Danke SlaterB!

import java.util.Vector;


public class Processor 

{
int x;
int y;
Vector<Integer> vector = new Vector<Integer>();
Object lock = new Object();

	Random r = new Random();
	
	void produce() throws InterruptedException 
	
	{
		while(true)
		{
			synchronized(lock)
			{
		
			x = r.nextInt(60);
			y = r.nextInt(60);
			System.out.println(x);
			System.out.println(y);
			
			Thread.sleep(100);
			
			System.out.println("Numbers generated");
			vector.add(x);
			vector.add(y);
			
			Thread.sleep(100);
			
			System.out.println("Vector created");
			
			lock.wait();
			lock.notify();
		
			}
		}
		
		
	}
	
	
	 void consume() throws InterruptedException
	
			{
		 while(true)
			{
		synchronized(lock)
		{
			for(int i = 0; i <= vector.get(0);i++)
			{
				 System.out.print("*");		
				 	
			}	
			
			System.out.print("
");
			
			
			for(int a = 0; a <= vector.get(1);a++)
			{
				 System.out.print("*");		   
				 
			}	
			
			System.out.print("
");
			
			Thread.sleep(100);
			vector.clear();
			System.out.println("Vector cleared");
			lock.notify();
			lock.wait();
		}
			}
		
		
			}
	
}

wait() wartet potentiell unendlich lange, notify() weckt andere auf,
die Reihenfolge solcher Befehle ist zu beachten

der erste Consumer weckt den Producer auf, geht schlafen,
der Producer weckt sofort den Consumer auf,
beide sind nun aktiv, Reihenfolge fast beliebig, aber Producer als erster aktivierter arbeitet sicher normalerweise als erster

wenn demnach als Standard erst Producer drankommt dann geht es noch gut, Consumer folgt direkt danach ohne erst noch neu informiert zu werden
(denn der Producer geht ja potentiell erstmal jahrelang in Winterschlaf, bevor er DANACH sein notify losschickt…)

geht es einmal andersrum dann findet der Consumer nix im Vector vor

@TO:
Schau dir doch der Einfachheit halber mal die API java.awt.image, vorzugsweise die Interfaces ImageProducer und ImageConsumer an. ImageProducer haben Methoden um ihnen ImageConsumer hinzuzufügen und davon zu entfernen. Die Vektoren sind wohl am besten beim Consumer aufgehoben. Während der Producer die einzelnen registrierten Consumer “bedient” (also deren Vektoren neu berechnet) können die Threads, auf denen die Consumer laufen, ebenso einzeln angehalten werden. Die Anweisung müsste dazu

  synchronized(c) {
    try {
      c.wait();
      // Vektor von c holen, bearbeiten und zurückschreiben
    } catch(InterruptedException e) {
      e.printStackTrace();
    } finally {
      c.notify();
    }
  }
}```
In der addConsumer-Methode kann der Producer geweckt werden, wenn der erste Consumer registriert wird und in der removeConsumer-Methode schlafen gelegt, wenn der letzte entfernt wurde.

das ist ja auch ein recht spezielles Modell, verschiedene Consumer einzeln bedient,

die klassische Variante dürfte sich um nur eine Liste, einen Vector drehen,
evtl. verschiedene Producer oder einer beizeiten mehrfach drankommend legen immer mehr hinein, evtl. bis Maximum

verschiedene Consumer, evtl. einer mehrfach drankommend, nehmen heraus, solange noch was da
Erzeuger-Verbraucher-Problem