Threads erzeugen Objekte und diese greifen auf globales Objekt zu.

articus

Grünschnabel
Hallo liebes Forum,

bisher habe ich nur passiv vom reichhaltigen Wissensschatz dieses Community profitiert, nun wende ich mich direkt an euch:

Ich habe folgende Situation: Verschiedene Threads erzeugen Objekte einer Klasse A (a1 bis an) - die Methoden dieser Objekte greifen nun auf ein globales Objekt B zu. (bzw. auf seine Methoden)

Ziel ist es den Zugriff der Methoden so zu synchronisieren, dass das globale Objekte immer nur von einem Thread und seinem Objekt ai benutzt werden kann. Die Methoden der Objekte (a1 bis an) sollen atomar am globalen Objekt B wirken.

Folgendes kann ich jedoch ausschließen: Es bringt nichts die Methoden der Klasse von B zu synchronisieren die von A verwendet werden, denn diese sind: read(); und write(); - sollten beide z.B. durch synchronized nur noch atomar wirken , nützt mir das wenig , weil immer noch Inkonsistenzen auftreten können.

Die Methoden von ai hingegen sollen beim Zugriff auf B synchronisiert sein. Natürlich nützt mir ein synchronized in A ebensowenig , da ja nicht der Zugriff auf die Methode von A gegen race conditions gesichert werden soll sondern der Zugriff auf das Objekt B.

An dieser dargestellten Struktur kann ich nichts ändern. Das Problem ist abstrakter Natur, ich kann darum auch leider keinen Code posten. Eine Lösung wird jedoch in Java implementiert.

Ich habe irgendwie keinen kritischen Bereich, den ich durch locks, semaphore etc. sichern könnte - d.h. ich möchte gerne den kompletten Zugriff auf das Objekt B (oder zumindest alle seine Variablen) gegen Mehrfachzugriff sperren können. Also B kann quasi immer nur von einem Thread benutzt werden.

Ich kenne im Betriebssystem von Windows NT die Möglichkeit, dass Threads sich bei Objekten anmelden können und in Warteschlangen auf den Zugriff warten bis sie den Zugang zum Objekt erhalten (Marshalling). Leider habe ich sowas nicht für Java gefunden oder bin zu blöd es zu sehen.

Ich habe ein Klassendiagramm eingefügt als Beispiel für eine solche Konstellation - hoffentlich hilfts.

objektsynchronisierung.jpg


Habt Ihr eine Idee wie das aussehen könnte?



Vielen Dank!
 
Guten Morgen articus,

schreib eine Klasse mit einer ArrayList und einem Boolean Wert, diese initialiesiert du für alle Threads global im Programm. In der ArrayList speicherst du die Namen deiner Strings ab sobald dieser auf die Methode zugreifen will und baust somit eine art Warteschleife. Jedes mal, wenn du einen Zugriff hast, setzt du den Boolean auf false und wenn dieser vorbei ist wieder auf true. So kannst du nacheinander alles abarbeiten.

Gruß Chris
 
Das was du brauchst findest du auch unter dem Begriff "Monitor" im Zusammenhang mit nebenläufiger Programmierung. Ein Beispiel in dem ein Monitor genutzt wird, sind die "Dining Philosophers".
 
Danke euch -

also dass mit den ArrayLists in der Warteschlange könnte funktionieren - das probiere ich dann mal-

Monitore bietet Java ja nativ an , das löst mein Problem aber nicht, denn wenn ich die Klasse, auf die zugegriffen wird zum Monitor mache, kann ich ihre Methoden natürlich zu kritischen Abschnitten machen.

Diese Methoden sind aber beide schon atomare Methoden (read und write) - aus einer Kombination von read und write kann ich aber trotzdem Inkonsistenzen am Datenobjekt schaffen wenn sie von Dritten verwendet werden. Der Zugriff auf die Mehtode ist zwar über den Monitor gesichert, d.h. die Methoden des Monitors werden immer nur von einem Thread verwendet -es können dennoch Inkonsistenzen entstehen.
 
Hey :)
Ich hatte vor kurzem in der Uni Threads und Synchronisation und hatte dort einige Problemchen, daher hab ich mich mal an deine Aufgabe gemacht :)
Ich hab einiges rumprobiert, aber für mich sieht das ganze ziemlich gut aus, wenn du ein synchronized auf die class variable von B setzt, und so die gesamte instanz sperrst.
Garantieren tu ich für nichts, viel Erfahrung hab ich damit selber nicht, es sieht nur nach mehrmaligen Ausgabeüberprüfungen ganz gut aus :D

Java:
public class B {
	
	private static int wert=0;
	private static final String pattern = "B -> %s";

	
	public static int read()
	{
		return wert;
	}
	
	public static void write(int i,String s)
	{
		synchronized(B.class)
		{
			int j = wert;
			wert=wert+i;
			System.out.println(String.format(pattern, s+" changed the Value from "+j+" to "+wert));
			System.out.println("--------------------------------------------------");
		}
	}
}

Java:
public class A {
	
	private static int number = 1;
	private final String name = "A["+A.number+"]";

	public A()
	{
		number++;
	}
	
	public void plusWert(int wert)
	{
		B.write(wert, name);
	}
	
	public void minusWert(int wert)
	{
		plusWert(-wert);
	}
}

Java:
import java.util.concurrent.TimeUnit;


public class MyThread implements Runnable{

	private A a = new A();
	
	@Override
	public void run() {

		for(int i =0; i <= 5;i++)
		{
			try{
				TimeUnit.MILLISECONDS.sleep(100);
				int j = (int) (Math.random()*2);
				int k = (int) (Math.random()*100);
				switch(j)
				{
					case 0: a.plusWert(k); break;
					case 1: a.minusWert(k); break;
					default: System.out.println("Fail"); break;
				}				
			}
			catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args){
		for(int i = 0; i <=10;i++)
			new Thread(new MyThread()).start();
	}
	

}



Edit: Nochmal bischen überarbeitet, sollte so glaube ich passen, kannst dich ja mal melden obs so klappt falls du das irgendwie genau testen kannst, ich kann da nur die Ausgaben bewerten ^^

Da read bei mir gar nicht benötigt wird, sollte da kein Problem auftreten. Falls du den wert aber wirklich erst in A auslesen willst, dann den neuen wert in A berechnen, und dann erst in B setzen, sind weitere synchronisationen nötig.
Es sollte so aber vollkommen ausreichen , da write die ganze instanz sperrt.
 
Zuletzt bearbeitet:
@I2oxxi

Ich wusste doch dass mir hier jemand helfen kann :) Vielen Dank dir! Ja du hast Recht die Ausgaben sagen nicht viel man muss es debuggen um festzustellen ob und wie gerade zugegriffen wird.

Damit kann ich dieses Thema nun beenden.
 
d.h. es funtioniert wirklich so? xd
ich hab nur bisl rumgetestet und es war ne vermutunt ^^

weil z.B.
Java:
public synchronized void write(int wert)
{
//code
}

ist das gleiche wie

Java:
public void write(int wert)
{
          synchronized(this)
          {
                    //CODE
          }
}

und ein this pointer in einer rein statisch orientierten klasse ist nicht so hilfreich :p
da aber jede klasse automatisch eine eigene class variable hat (kenns nur durch reflection :p)
kann man so die instanz sperren (vermutung, wenn du sags es funktioniert so ist es wohl richtig ^^)

edit::

nach einigem nachlesen hab ich jetz rausgefunden das
Java:
public static synchronized void write(int wert)
{

}

wohl auch das gleiche ist wie
Java:
public staic void write (int wert)
{
    synchronized(B.class)
    {

    }
}

sollte also eigentlich das gleiche bei rauskommen, und du sagtest ja im ersten post das das synchronized in den methoden von b nix bringt :s
 
Zuletzt bearbeitet:
richtig, ist das gleiche :)

Shit,ne geht echt nicht - aber wir arbeiten hier grade erst an der UML, darum hatte ich kein Implementierungsfähiges Programm vorliegen in dem ich das konkret testen konnte. Ich befürchte du sperrst wieder nur write seperat ab.

Meine Idee ist jetzt dafür zu sorgen, dass immer wenn B`s Methoden read ODER write aufgerufen werden als erstes ALLE Methoden von B gesperrt werden.

-> dass ginge z.B. über eine Mutex-Variable die sich auf beide Methoden bezieht oder einen globalen Semaphor der von allen Methoden benutzt wird. Also wenn read durchgeführt wird, wird der Zugriff auf write blockiert - auf read aber auch! So dass Inkonsistenzen ausgeschlossen werden können. Solange quasi eine der beiden Methoden benutzt wird, können beide nicht benutzt werden.
 
Zurück