Semafory Java

0

Witam, mam za zadanie (semafory) stworzyć pętle 30 linii, gdzie w każdej ma się pojawić CBACBADDE. Prowadzący zajęcia powiedział, że niewiele mi brakuje do poprawnego zadania, a ja pomimo wielu prób nie potrafię go poprawnie zmodyfikować. Z góry dziękuję za pomoc.

'''
package sample;

import java.util.concurrent.Semaphore;
public final class Controller {
private static final int COUNT = 30;
private static int COUNTER = 0;
private static final int STEP = 9;
private static final Semaphore a = new Semaphore(0, true);
private static final Semaphore b = new Semaphore(0, true);
private static final Semaphore c = new Semaphore(1, true);
private static final Semaphore d = new Semaphore(0, true);
private static final Semaphore e = new Semaphore(0, true);

public static void main(String[] args) {
    new A().start();
    new B().start();
    new C().start();
    new D().start();
    new E().start();
}

private static final class A extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                a.acquire();
               // b.release();
              //  a.acquire();
                myPrint("A ");
                c.release();
                d.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}

private static final class B extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                b.acquire();
                myPrint("B ");
                a.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}

private static final class C extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                c.acquire();
                myPrint("C ");
                b.release();


            }
        }
        catch (InterruptedException ex) {
        }
    }
}

private static final class D extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                d.acquire();
                c.release();
                d.acquire();
                myPrint("D ");
                e.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}
private static final class E extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT; i++) {
                e.acquire();
                myPrint("E ");
                c.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}
private static synchronized void myPrint(String s) {
    COUNTER++;
    System.out.print(s);
    if (COUNTER == STEP) {
        COUNTER = 0;
        System.out.println();
    }
}

}
'''java

1

Jaka jest dokładnie treść zadania? Każdej pozycji w ciągu można przyporządkować osobny semafor tak, aby wszystko zsynchronizować (tablica semaforów).

Można tez wypisywać w pętli „CBACBADDE” ale raczej nie o to chodzi xD podaj treść.

0

Jest prawie dobrze, ale trochę przekombinowałeś z A i D. Dla A nie powinieneś iterować COUNT * 2 razy, ponieważ za drugim razem przechodzisz do D a nie do C. Otwieranie jednocześnie semafora c i d nie jest dobrym pomysłem:

                // A
                for (int i = 0; i < COUNT; i++) {
                    a.acquire();
                    myPrint("A ");
                    c.release();
                    a.acquire();
                    myPrint("A ");
                    d.release();
                }

I D się znacznie upraszcza:

                // D
                for (int i = 0; i < COUNT * 2; i++) {
                    d.acquire();
                    myPrint("D ");
                    myPrint("D ");
                    e.release();
                }
0
Charles_Ray napisał(a):

Jaka jest dokładnie treść zadania? Każdej pozycji w ciągu można przyporządkować osobny semafor tak, aby wszystko zsynchronizować (tablica semaforów).

Można tez wypisywać w pętli „CBACBADDE” ale raczej nie o to chodzi xD podaj treść.

Stosując semafory zmodyfikuj plik ABCwithSem.java tak, by na standardowym
wyjściu (konsoli) 30 razy pojawiał się napis: CBACBADDE. Tylko jeden wątek wypisuje jedną literę, tj. tylko wątek A wypisuje literę A. W jednej iteracji pętli for(…) można tylko raz wywołać metodę myPrint(…) Pojedyncze wywołanie metody myPrint(…) wypisuje tylko jedną literę

0
damianem napisał(a):

Jest prawie dobrze, ale trochę przekombinowałeś z A i D. Dla A nie powinieneś iterować COUNT * 2 razy, ponieważ za drugim razem przechodzisz do D a nie do C. Otwieranie jednocześnie semafora c i d nie jest dobrym pomysłem:

                // A
                for (int i = 0; i < COUNT; i++) {
                    a.acquire();
                    myPrint("A ");
                    c.release();
                    a.acquire();
                    myPrint("A ");
                    d.release();
                }

I D się znacznie upraszcza:

                // D
                for (int i = 0; i < COUNT * 2; i++) {
                    d.acquire();
                    myPrint("D ");
                    myPrint("D ");
                    e.release();
                }

Dzieki za odpowiedź, ale zapomniałem dodać, że tylko jeden wątek wypisuje jedną literę, tj. tylko wątek A wypisuje literę A. W jednej iteracji pętli for(…) można tylko raz wywołać metodę myPrint(…) Pojedyncze wywołanie metody myPrint(…) wypisuje tylko jedną literę

0
Charles_Ray napisał(a):

Jaka jest dokładnie treść zadania? Każdej pozycji w ciągu można przyporządkować osobny semafor tak, aby wszystko zsynchronizować (tablica semaforów).

Można tez wypisywać w pętli „CBACBADDE” ale raczej nie o to chodzi xD podaj treść.

'''java
import java.util.concurrent.Semaphore;

public final class ABCwithSem {
private static final int COUNT = 30;
private static int COUNTER = 0;
private static final int STEP = 3;
private static final Semaphore a = new Semaphore(1, true);
private static final Semaphore b = new Semaphore(0, true);
private static final Semaphore c = new Semaphore(0, true);

public static void main(String[] args) {
    new A().start();
    new B().start();
    new C().start();
}

private static final class A extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT; i++) {
                a.acquire();
                myPrint("A ");
                b.release();
            }
        } catch (InterruptedException ex) {
        }
    }
}

private static final class B extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT; i++) {
                b.acquire();
                myPrint("B ");
                c.release();
            }
        } catch (InterruptedException ex) {
        }
    }
}

private static final class C extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT; i++) {
                c.acquire();
                myPrint("C ");
                a.release();
            }
        } catch (InterruptedException ex) {
        }
    }
}

private static synchronized void myPrint(String s) {
    COUNTER++;
    System.out.print(s);
    if (COUNTER == STEP) {
        COUNTER = 0;
        System.out.println();
    }
}
1
  • Ustaw początkową wartość semafora c na 3
  • Wymagaj w pętli C na początku dwóch tokenów -> po pierwszym wypisanym C wartość semafora spadnie do 1 -> pierwsze A doda 1, będzie w sumie 2 co sprawi, że CBA wypisze się jeszcze raz. Drugie A znowu doda jeden do c, ale tym razem będzie to za mało żeby znowu uruchomić sekwencję CBA (czyli dobrze, bo teraz chcemy przejść do DDE).
  • Z pętli D wywal c.release() -> zamiast tego dodaj na końcu pętli E c.release(2) żeby przywrócić stan początkowy przed kolejną linijką
  • Pętla E powinna dodawać do d wartości tak, żeby wymusić wypisanie drugiego D, czyli coś takiego:
                // E
                for (int i = 0; i < COUNT; i++) {
                    e.acquire();
                    d.release(2);
                    e.acquire();
                    myPrint("E ");
                    c.release(2);
                }
0
damianem napisał(a):
  • Ustaw początkową wartość semafora c na 3
  • Wymagaj w pętli C na początku dwóch tokenów -> po pierwszym wypisanym C wartość semafora spadnie do 1 -> pierwsze A doda 1, będzie w sumie 2 co sprawi, że CBA wypisze się jeszcze raz. Drugie A znowu doda jeden do c, ale tym razem będzie to za mało żeby znowu uruchomić sekwencję CBA (czyli dobrze, bo teraz chcemy przejść do DDE).
  • Z pętli D wywal c.release() -> zamiast tego dodaj na końcu pętli E c.release(2) żeby przywrócić stan początkowy przed kolejną linijką
  • Pętla E powinna dodawać do d wartości tak, żeby wymusić wypisanie drugiego D, czyli coś takiego:
                // E
                for (int i = 0; i < COUNT; i++) {
                    e.acquire();
                    d.release(2);
                    e.acquire();
                    myPrint("E ");
                    c.release(2);
                }

Dzięki za odpowiedz, rozumiem, że kod powinien wyglądać, tak? Pytam się. ponieważ nadal nie wychodzi mi pożądany wynik.
```Java
public final class Controller {

private static final int COUNT = 30;
private static int COUNTER = 0;
private static final int STEP = 9;
private static final Semaphore a = new Semaphore(0, true);
private static final Semaphore b = new Semaphore(0, true);
private static final Semaphore c = new Semaphore(3, true);
private static final Semaphore d = new Semaphore(0, true);
private static final Semaphore e = new Semaphore(0, true);

public static void main(String[] args) {
    new A().start();
    new B().start();
    new C().start();
    new D().start();
    new E().start();
}

private static final class A extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                a.acquire();
                // b.release();
                  //a.acquire();
                myPrint("A ");
                c.release();
                d.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}

private static final class B extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                b.acquire();
                myPrint("B ");
                a.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}

private static final class C extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                c.acquire();
                myPrint("C ");
                b.release();

            }
        }
        catch (InterruptedException ex) {
        }
    }
}

private static final class D extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT * 2; i++) {
                d.acquire();
                //c.release();
                d.acquire();
                myPrint("D ");
                e.release();
            }
        }
        catch (InterruptedException ex) {
        }
    }
}
private static final class E extends Thread {

    @Override
    public void run() {
        try {
            for (int i = 0; i < COUNT; i++) {
                e.acquire();
                d.release(2);
                e.acquire();
                myPrint("E ");
                c.release(2);
            }
        }
        catch (InterruptedException ex) {
        }
    }
}
private static synchronized void myPrint(String s) {
    COUNTER++;
    System.out.print(s);
    if (COUNTER == STEP) {
        COUNTER = 0;
        System.out.println();
    }
}

}

3

W C zamień c.acquire() na c.acquire(2)

2

Mogę zapytać, dlaczego wartość semaforowa c ma wartość 3?

Chodzi o to, żeby na końcu pierwszej sekwencji CBA odpalić C ponownie, ale za drugim razem już nie - wtedy chcemy zacząć pisać DDE. Skoro jednak pętla A ma wykonywać za każdym razem ten sam kod to możemy takie zachowanie wymusić kombinując z wartością semafora. Jeśli odpalenie C wymaga 2 wartości a A dodaje jedną, to łatwo policzyć, że przed każdą linijką wartość c powinna wynosić 3 - pierwsze odpalenie C zabiera 2, zostaje 1. Pierwsze odpalenie A dodaje 1, mamy 2. To pozwala na odpalenie C ponownie, c spada do wartości 0 -> drugie A dodaje do c 1, mamy w sumie 1. W tym momencie C się nie odpali, ale za to wartość semafora d osiągnęła 2 co pozwala na rozpoczęcie sekwencji DDE. Na końcu trzeba c podnieść o 2, żeby znowu wartość wynosiła 3.

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