tifyty

pure Java, what else ?

Tudásteszt multi thread, 2 megoldás

Ez nem egy önálló poszt, hanem a bejegyzés feladatának megoldása. Ha azt a bejegyzést még nem olvastad el, akkor inkább kezdd ott.

Az volt a feladat, hogy írjunk egy olyan Dream osztályt, amelyik a

package sleep;
 
import dream.Dream;
 
public class Sleeper {
    private int level;
 
    public synchronized int enter(Dream dream) {
        level++;
        try {
            dream.dream(this);
        } finally {
            level--;
        }
        return level;
    }
}

valamint

package sleep;
 
import dream.Dream;
 
public class Main {
    public static void main(String[] args) {
        if (new Sleeper().enter(new Dream()) != 0) {
            // The goal is to reach this line
            System.out.println("Am I still dreaming?");
        }
    }
}

osztályok esetén úgy viselkedik, hogy a fő osztályban a main metódus kiírja az Am I still dreaming? kérdést. Ehhez adtam azt a hintet, hogy egy synchronized metódust egy időben csak egy szál futtathat, de ahhoz, hogy egy szál elkezdhesse futtatni a metódust NEM KELL, hogy a futó szál befejezze a metódust, elég ha félbehagyja a futtatást valamilyen módon: végrehajt egy wait() hívást. Egy kicsit virágnyelven fogalmaztam a tipp oldalon, de ez volt a lényeg. Innen kezdve már elég triviális a megoldás (vagy nem).

A hivatalos megoldás a postban hivatkozott oldalon:

package dream;
 
import sleep.Sleeper;
 
public class Dream {
    private static void doWait(Sleeper s) {
        try {
            s.wait(200);
        } catch (InterruptedException e) {
        }
    }
 
    public void dream(final Sleeper s) {
        new Thread() {
            public void run() {
                s.enter(new Dream() {
                    @Override
                    public void dream(Sleeper s) {
                        doWait(s);
                    }
                });
            }
        }.start();
 
        doWait(s);
    }
}

A lényeget ez el is mondja, de mivel nem garantált, hogy ez a verzió helyesen lefut (de azért többnyire kiírja a csodálkozó kérdést, ha viszont mindenféle System.out.println-okat tesz bele az ember, akkor a 200ms elég kevés lehet és időzítési problémái akadhatnak), ezért egy cseppet módosított változatot tárgyalok. Akit érdekelnek további megoldások, azok olvassák el az eredeti blog kommentjeit, van belőlük több, mint száz. Szóval a módosított megoldás szerint:

package dream;

import sleep.Sleeper;

public class Dream {
	private volatile boolean otherThreadNotStarted = true;
	private Thread mainThread = Thread.currentThread();

	private static void doWait(Sleeper s) {
		try {
			s.wait(1);
		} catch (InterruptedException e) {
		}
	}

	public void dream(final Sleeper s) {
		Thread t = new Thread() {
			public void run() {
				s.enter(new Dream() {
					@Override
					public void dream(Sleeper s) {
						otherThreadNotStarted = false;
						while (mainThread.isAlive()) {
							doWait(s);
						}
					}
				});
			}
		};
		t.start();
		while (otherThreadNotStarted) {
			doWait(s);
		}
	}
}

A főprogram meghívja a dream metódust a 16. sorban, ami létrehoz egy új szálat és el is indítja. Persze semmi nem garantálja, hogy ez a szál el is indul rögtön. Lehet, hogy csak egy hét múlva indul el. A Java nem real-time rendszer. Ezért, mielőtt visszatérne a fő szál az álomból elkezd várni. Csak vár, és vár, és amíg vár elengedi a monitort, ami annak megakadályozására szolgál, hogy más szál ne futtathassa az enter metódust. Persze nem vár sokáig, de ha a másik szál még nem indult el, vagy ugyan elindult, de még nem jelezte a futását az otherThreadNotStarted változóban, akkor újra vár.

A java futtató rendszer látva, hogy ez a mi fő szálunk ilyen makacs, előbb utóbb persze elindítja a másik szálat is, és ha nem olyan csökönyös a JVM, no meg az operációs rendszer alatta, akkor erre nem kell a valóságban egy hetet várnunk. Elindul a másik szál, és belép az enter metódusba, mégpedig ugyanazon az ojjektumon, amelyikben a fő szál is éppen benne van. (Na, erre nem is gondoltam, amikor Arankát emlegettem. Ez egy kicsit erős, de most már mindegy.)

Nem biztos, hogy sikerül neki! Miért? Azért, mert közben a fő szál fel fel ébredget, és ha éppen éber, akkor nem engedi, hogy a másik szál is bejusson. (Ez egyre rosszabb.) De mivel a fő szál mással sincs elfoglalva, mint hogy újra és újra elbóbiskoljon (öregember üzemmód), és ezzel újra és újra elengedje a monitort, ezért egyszer csak a másik szál bejut az enter metódusba, és az meghíjja az annak átadott Dream-mel kompatibilis, abból leszármazott anoním osztály (amely, mint az köztudott hozzáfér a lokális (és így az argumentum) változókhoz (is), amennyiben azok final változók) dream metódusát (jaaaaaajjjjj!!!!). Ha meg már benne vagyunk, és álmodunk, most már mind a két szálon, akkor akár engedhetjük is, hogy a fő szál felébredve ne tentikéljen, hanem húzzon vissza a fő metódusba, írja ki amit ki kell írni magából és minekutána más dolga nincs ezen a JVM-en: termináljon. Másra sem vár a második szál, mivelhogy éppen, hogy erre vár, és amikor ez megtörtént, akkor, minthogy dolga neki is bevégeztetett ő is vissza-/megtér.

Hát ennyi.

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s

%d blogger ezt kedveli: