Synchronizacja - wlasciciel monitora

0

Mam generalnie taki problem.

Jeden watek jest odpowiedzialny z wczytywanie z pliku po kolei liczb i powiadomienie działających wątków, że liczba się zmieniła. Wtedy każdy z wątków powinien się wybudzić, sprawdzić czy pobrana liczba jest równa nazwie tego wątku (1,2,3,4,5....), jeśli tak to wypisuje swoja nazwe na ekran. Jeśli nie to zasypia bez wypisywania i czeka dalej. A wiec:

Watek odpowiedzialny za pobranie liczby z pliku:

public class Losuj extends Thread{
	
	
		public void run(){
			
			try{
				File f = new File("test.txt");                              //otwieramy plik
				Scanner scaner = new Scanner(f);
				
	
				while(scaner.hasNext()){                                  //dopoki w pliku sa liczby
					synchronized(n){	                                   //wszystkie watki spia
						n = scaner.nextInt();                      //pobieramy liczbe
						System.out.println("Losuje nowa liczbe " + n);
						n.notifyAll();                                 // daje znac spiacym watka ze liczba wylosowana
					}                                                         // i wychodze z bloku dajac szanse pozostalym watka aby
				}                                                                 // podzialaly na zmiennej n    !!! wlasnie tu sie program 
		                                                                                  // wysypuje
			catch (FileNotFoundException ex){
				
			}
		}
	} 

Watek wypisujacy swoja nazwe:

public class Watek3 extends Thread{
		
		private Integer _name;
		
		public Watek3(int name){
			_name = new Integer(name);
		};
		
		public void run(){
			while(true){
				try{
					synchronized(n){
						if (_name.equals(n)){
							System.out.println("!!!!!!!!!!!!!!!!!!Jestem watkiem nr " + _name +"   "+ n);
						}
						System.out.println("Spie " + _name);
						n.wait();
					}
				}
				catch (InterruptedException ex){
					
				}
			}
		}

	} 

n to zmienna globalna

W watku Losuj przy notifyAll() leci wyjatek IllegalMonitorStateException. Nie łapie dlaczego? Przecież wszystkie wątki wypisujace spią tzn że monitor jest wolny, więc program bez problemu wchodzi do bloku synchronized w watku Losuj i niby czemu nie jest wtedy wlascicielem monitora?

Bylabym wdzieczna jeśli byłby ktoś w stanie pomóc

1
synchronized(n){
    n = scaner.nextInt();
    //...
}
0

Tu masz podobny przykład z wybudzaniem wątków, ale zamiast z pliku są one losowane z jakiegoś zbioru (wystarczy przerobić metodę Detonator.wczytajLiczbę()). Wątki uruchamiają się w losowej kolejności i działają losowy czas. Tak samo losowo są wybudzane.
Sekcje krytyczne są na wątkach dla potrzeb wait/notifyAll oraz na wątku uruchamiającym ze względu na konieczny dostęp do jego pól.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class Detonator
{
	public static void main(String[] args)
		throws InterruptedException
	{
		Detonator detonator = new Detonator();
		detonator.test();
	}

	private static class Wątek extends Thread
	{
		public Wątek(int nr, Detonator monitor)
		{
			this.monitor = monitor;
			this.setName(Integer.toString(nr));
			System.out.println("Utworzono wątek nr " + getName());
		}

		@Override public void run()
		{
			String komunikat = "Wątek " + this.getName() + " został ";
			System.out.println(komunikat + "uruchomiony...");
			Detonator.sleep(losuj.nextInt(1000));
			System.out.println(komunikat + "uśpiony");
			try
			{
				waitForWake(); //tu wątek idzie w kimę
				komunikat += "wybudzony."; //tu już po przebudzeniu
			}
			catch(InterruptedException ex) { komunikat += "przerwany."; }
			System.out.println(komunikat);
			monitor.wybudzono(this); //informuje o swoim przebudzeniu
			Detonator.sleep(losuj.nextInt(1000)); //zakończy po losowym czasie
		}

		//synchronizacja na obiekcie wątku dla potrzeb wait na wątku
		private synchronized void waitForWake() throws InterruptedException
		{
			final int nrWątku = Integer.parseInt(getName());
			//ustala warunki przebudzenia
			//negacja przebudzenia warunkuje oczekiwanie
			while(!(monitor.getDoWybudzenia() == nrWątku
				//nie wybudzać przed zarejestrowaniem wybudzenia innego wątku
				&& monitor.wybudzono == null))
					this.wait();
			//wątek przebudzony
		}

		private final Detonator monitor;
	};

	public void test() throws InterruptedException
	{
		System.out.println("Tworzenie wątków...");
		for(int nr = 0; nr < ilośćWątków; ++nr)
			wątki.add(new Wątek(nr, this));
		Collections.shuffle(wątki);
		sleep(1000);
		System.out.println("\nUruchamianie wątków...");
		for(Wątek wątek: wątki)
			wątek.start();
		sleep(1000);
		System.out.println("\nLosowe próby wybudzania wątków...");
		wybudzanie();
		System.out.println("Wszystkie wątki wybudzone i zakończone.");
	}

	private void wybudzanie()
	{
		for(int nrLosowania = 0; wątki.size() > 0; ++nrLosowania)
		{
			//synchronizacja dla dostępu do pól Detonatora
			synchronized(this) //tylko co czwarte trafienie może być dobre
				{ doWybudzenia = wczytajLiczbę(); }
			//nie wolno modyfikować listy "wątki" podczas przeglądania w innym
			//wątku bo pojawi się ConcurrentModificationException
			for(Wątek wątek: wątki)
				//synchronizacja na wątku dla potrzeb notifyAll()
				synchronized(wątek) { wątek.notifyAll(); }
			synchronized(this) //synchronizacja dla dostępu do pól Detonatora
			{	//wątek nie może sam siebie usunąć z listy wątki
				if(wybudzono != null) //ostatnio wybudzony wątek zostawił ślad?
				{	//usuwanie ostatnio wybudzonego wątku
					wątki.remove(wybudzono);
					wybudzono = null; //dla pozostałych wątków
					System.out.printf("Wybudzono go po %d losowaniach.%n",
						nrLosowania);
				}
			}
			sleep(10); //pozwala na lepsze losowanie
		}
	}

	//wybór numeru następnego wątku do wybudzenia
	private int wczytajLiczbę() { return losuj.nextInt(4 * ilośćWątków); }

	//synchronizacja dla dostępu dla pola detonatora
	private synchronized int getDoWybudzenia() { return doWybudzenia; }

	//synchronizacja dla dostępu dla pola detonatora
	private synchronized void wybudzono(Wątek w) { wybudzono = w; }

	//wyeliminowanie try/catch dla krótszego zapisu
	private static void sleep(final int milisec)
	{
		try { Thread.sleep(milisec); } catch(InterruptedException ignore) {}
	}

	private static final int ilośćWątków = 20;
	private static Random losuj = new Random();
	private final List<Wątek> wątki = new ArrayList<>(ilośćWątków);
	private int doWybudzenia = -1; //nr wątku do wybudzenia
	private Wątek wybudzono = null; //ostatnio wybudzony wątek
}

Przykładowy wynik:
Tworzenie wątków...
Utworzono wątek nr 0
Utworzono wątek nr 1
Utworzono wątek nr 2
Utworzono wątek nr 3
Utworzono wątek nr 4
Utworzono wątek nr 5
Utworzono wątek nr 6
Utworzono wątek nr 7
Utworzono wątek nr 8
Utworzono wątek nr 9
Utworzono wątek nr 10
Utworzono wątek nr 11
Utworzono wątek nr 12
Utworzono wątek nr 13
Utworzono wątek nr 14
Utworzono wątek nr 15
Utworzono wątek nr 16
Utworzono wątek nr 17
Utworzono wątek nr 18
Utworzono wątek nr 19

Uruchamianie wątków...
Wątek 6 został uruchomiony...
Wątek 5 został uruchomiony...
Wątek 14 został uruchomiony...
Wątek 18 został uruchomiony...
Wątek 2 został uruchomiony...
Wątek 17 został uruchomiony...
Wątek 9 został uruchomiony...
Wątek 12 został uruchomiony...
Wątek 3 został uruchomiony...
Wątek 19 został uruchomiony...
Wątek 10 został uruchomiony...
Wątek 4 został uruchomiony...
Wątek 16 został uruchomiony...
Wątek 0 został uruchomiony...
Wątek 7 został uruchomiony...
Wątek 11 został uruchomiony...
Wątek 13 został uruchomiony...
Wątek 8 został uruchomiony...
Wątek 1 został uruchomiony...
Wątek 15 został uruchomiony...
Wątek 1 został uśpiony
Wątek 7 został uśpiony
Wątek 3 został uśpiony
Wątek 6 został uśpiony
Wątek 8 został uśpiony
Wątek 11 został uśpiony
Wątek 18 został uśpiony
Wątek 14 został uśpiony
Wątek 16 został uśpiony
Wątek 13 został uśpiony
Wątek 10 został uśpiony
Wątek 2 został uśpiony
Wątek 15 został uśpiony
Wątek 12 został uśpiony
Wątek 0 został uśpiony
Wątek 19 został uśpiony
Wątek 17 został uśpiony
Wątek 4 został uśpiony
Wątek 5 został uśpiony
Wątek 9 został uśpiony

Losowe próby wybudzania wątków...
Wątek 4 został wybudzony.
Wybudzono go po 3 losowaniach.
Wątek 10 został wybudzony.
Wybudzono go po 7 losowaniach.
Wątek 15 został wybudzony.
Wybudzono go po 9 losowaniach.
Wątek 5 został wybudzony.
Wybudzono go po 10 losowaniach.
Wątek 16 został wybudzony.
Wybudzono go po 18 losowaniach.
Wątek 9 został wybudzony.
Wybudzono go po 19 losowaniach.
Wątek 8 został wybudzony.
Wybudzono go po 29 losowaniach.
Wątek 3 został wybudzony.
Wybudzono go po 35 losowaniach.
Wątek 7 został wybudzony.
Wybudzono go po 39 losowaniach.
Wątek 17 został wybudzony.
Wybudzono go po 45 losowaniach.
Wątek 6 został wybudzony.
Wybudzono go po 49 losowaniach.
Wątek 13 został wybudzony.
Wybudzono go po 55 losowaniach.
Wątek 19 został wybudzony.
Wybudzono go po 59 losowaniach.
Wątek 12 został wybudzony.
Wybudzono go po 65 losowaniach.
Wątek 2 został wybudzony.
Wybudzono go po 87 losowaniach.
Wątek 0 został wybudzony.
Wybudzono go po 157 losowaniach.
Wątek 11 został wybudzony.
Wybudzono go po 165 losowaniach.
Wątek 18 został wybudzony.
Wybudzono go po 169 losowaniach.
Wątek 14 został wybudzony.
Wybudzono go po 183 losowaniach.
Wątek 1 został wybudzony.
Wybudzono go po 193 losowaniach.
Wszystkie wątki wybudzone i zakończone.

1 użytkowników online, w tym zalogowanych: 0, gości: 1