Zadanie 4. Problem obiadujących kanibali.
Plemię kanibali spożywa wspólny posiłek z kotła mieszczącego M misjonarzy. Jeden z kanibali ustala liczbę m nie większą niż M misjonarzy, których chce zjeść i jeśli jest ich tylu w kotle to przystępuje do konsumpcji. W przeciwnym wypadku budzi wioskowego kucharza, który napełnia kocioł kolejnymi misjonarzami, o ile tylu misjonarzy jest dostępnych, a jak nie to czeka. Ja się plemię zdenerwuje bo za długo czeka na misjonarzy to dorzuca kucharza, spożywają wszystkich obecnych w kotle i program się kończy.
ZADANIE MOŻNA RÓŻNIE ZINTERPRETOWAĆ, WIĘC NIE WIEM CZY TO JEST DOBRZE ZROBIONE.
W moim rozwiązaniu Kucharz działa w jednym wątku, a egzekutor ma pule Kanibali którzy pojedynczo (bo egzekutor jest ustawiony w tryb gdzie odpala jeden wątek naraz, a reszta czeka aż on się wykona) próbują się najeść. Każdy patrzy czy jest tylu misjonarzy w kotle ilu chce zjeść, jak nie to budzi Kucharza i 'odchodzi’ (daje wait(), żeby nie blokować funkcji uzupełniania kotła, wtedy odpala się Kucharz). Kucharz napełnia kocioł i idzie znowu spać (daje wait(), żeby nie blokować kotła i pozwolić kanibalowi jeść) i tak do czasu aż się skończą misjonarze. Po za komentowaniu paru linii w Main.cpp i od komentowaniu paru program działa inaczej i wszyscy Kanibale chcą się naraz dorwać do kotła, ale wtedy nie ma pewności, że naje się ten który obudzi Kucharza, bo kto inny może podejść pierwszy do kotła po napełnieniu (losowo).
A od strony technicznej wygląda to tak, że kocioł ma funkcje 'wrzucDoKotla’ i 'zjedz’ które są synchronizowane, więc tylko jeden wątek z wszystkich działających może używać jednej z nich. Więc nie ma obaw, że kocioł będzie naraz opróżniany i napełniany. Jednak, żeby Kucharz nie napełniał kotła w losowych sytuacjach, a jedynie po tym jak inny wątek go obudzi dodałem obiekt public static Double budzik = 1d; w Kucharzu oraz funkcje do budzenia i przechodzenia w stan spoczynku (zablokowany / wait). Kucharz po napełnieniu kotła idzie spać (używa doWait() blokujące sie na budzik), a Kanibal używa 'doNotify()’ na Kucharzu kiedy w kotle nie ma dość misjonarzy, żeby odblokować wątek kucharza (używa doNotify() odblokowujące wątek zablokowany [który użył wait() ] na obiekcie budzik ).
Więcej na temat 'monitorowania’ (funkcje wait/notify ma każdy obiekt) można znaleźć na:
http://www.journaldev.com/1037/java-thread-wait-notify-and-notifyall-example
Dodam tylko, że monitorowania należy używać na utworzonych obiektach [ new Costam() ] , a nie NULLach i nie powinien to być obiekt o wartości domyślnej (podobno), czyli np. pusty string czy zero dla liczb.
Można pobrać pliki w .zip: ZSO_PROJEKT_1_ZADANIE_4
A obejrzeć tutaj:
Main.java
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class Main { static int iloscKanibali = 15; static int pojemnoscKotla = 10; static int iloscMisjonarzy = 100; static int czasZanimZjeKucharza = 3000; static KotloKlatka KotloKlatka = new KotloKlatka(); static Kucharz Kucharz = new Kucharz(); public static void main(String[] args) throws InterruptedException { KotloKlatka.setKlatkaIloscMisjonarzy(iloscMisjonarzy); KotloKlatka.setKociolPojemnosc(pojemnoscKotla); // WERSJA Z JEDNYM BUDZACYM // kucharz idzie spac, kociol jest pusty Kucharz.start(); // kanibale zaczynaja uczte ExecutorService zarzadzcaUczty = Executors.newSingleThreadExecutor(); // WERSJA Z WIELOMA BUDZACYMI /* ExecutorService zarzadzcaUczty = Executors.newFixedThreadPool(iloscKanibali+1); zarzadzcaUczty.submit(Kucharz); */ for(int i = 0; i < iloscKanibali; i++) { zarzadzcaUczty.submit(new Kanibal(i)); } zarzadzcaUczty.shutdown(); zarzadzcaUczty.awaitTermination(9999, TimeUnit.HOURS); System.out.println("KONIEC"); Kucharz.interrupt(); // BEZ SYSTEM EXIT: WTF //System.exit(1); } }
KotloKlatka.java
public class KotloKlatka { private int kociolIloscMisjonarzy = 0; private int kociolPojemnosc = 10; private int klatkaIloscMisjonarzy = 100; public synchronized void wrzucDoKotla() { System.out.println("KUCHARZ: DOKLADAM MISJONARZY"); while(kociolIloscMisjonarzy < kociolPojemnosc) { if(klatkaIloscMisjonarzy == 0) { System.out.println("KUCHARZ: BRAK MISJONARZY"); break; } klatkaIloscMisjonarzy--; kociolIloscMisjonarzy++; } } public synchronized void zjedz(int iloscDoZjedzenia, int idKanibala) throws InterruptedException { System.out.println("KANIBAL " + idKanibala + ": ZACZYNAM JESC " + iloscDoZjedzenia + " LUDZI"); long start = System.currentTimeMillis(); while(start + Main.czasZanimZjeKucharza > System.currentTimeMillis()) { if(kociolIloscMisjonarzy >= iloscDoZjedzenia) { Thread.sleep(50 * iloscDoZjedzenia); // czas zjadania kociolIloscMisjonarzy -= iloscDoZjedzenia; System.out.println("KANIBAL " + idKanibala + ": ZJADLEM " + iloscDoZjedzenia); return; } else { Main.Kucharz.obudzKucharza(idKanibala); wait(200); } } System.out.println("KANIBAL " + idKanibala + ": " + Main.czasZanimZjeKucharza + " MS CZEKAM I NADAL PUSTY KOCIOL!"); System.out.println("KANIBAL " + idKanibala + ": ZJADAM KUCHARZA"); System.exit(1); } public int getKociolIloscMisjonarzy() { return kociolIloscMisjonarzy; } public void setKociolIloscMisjonarzy(int kociolIloscMisjonarzy) { this.kociolIloscMisjonarzy = kociolIloscMisjonarzy; } public int getKlatkaIloscMisjonarzy() { return klatkaIloscMisjonarzy; } public void setKlatkaIloscMisjonarzy(int klatkaIloscMisjonarzy) { this.klatkaIloscMisjonarzy = klatkaIloscMisjonarzy; } public int getKociolPojemnosc() { return kociolPojemnosc; } public void setKociolPojemnosc(int kociolPojemnosc) { this.kociolPojemnosc = kociolPojemnosc; } }
Kanibal.java
import java.util.Random; public class Kanibal implements Runnable { public int id = -1; public static Random random = new Random(); Kanibal(int id) { this.id = id; } @Override public void run() { try { Main.KotloKlatka.zjedz(random.nextInt(Main.KotloKlatka.getKociolPojemnosc()) + 1, this.id); } catch (InterruptedException e) { e.printStackTrace(); } } }
Kucharz.java
public class Kucharz extends Thread { public static Double budzik = 1d; @Override public void run() { while(true) { System.out.println("KUCHARZ: IDE SPAC"); doWait(); // czekaj az ktos wysle info Main.KotloKlatka.wrzucDoKotla(); } } public void doWait(){ synchronized(budzik){ try{ budzik.wait(); } catch(InterruptedException e){} } } public void doNotify(){ synchronized(budzik){ budzik.notify(); } } public void obudzKucharza(int idKanibala) { System.out.println("KUCHARZ: KANIBAL " + idKanibala + " MNIE BUDZI"); doNotify(); } }