Problem z wielowątkowością

0

Witam!

Mam taki problem: chciałbym napisać program wielowątkowy, składający się z czterech wątków, z których każdy drukuje jedną literę (A, B, C, D) po czym śpi przez losowy okres czasu z tym, że w żadnym momencie ilość liter A i B, nie może przekroczyć ilości liter C i D, ilość liter A nie może przekroczyć dwukrotności liter B, a jeśli wydrukowana została litera C, to kolejna może pojawić się po co najmniej jednej literze D. Do komunikacji chciałbym wykorzystać metody wait(), notify(), notifyAll(). Próbowałem zacząć od prostego programu:

[code]

public class Literki
{
public synchronized static void main (String args[])
{
Watek watek = new Watek();
watek.start();
}
}

class Watek extends Thread
{
private int licznikA;
private int licznikB;
private int licznikC;
private int licznikD;
private boolean czyC;
private Object rygiel;
WatekA watekA;
WatekB watekB;

public Watek()
{
	licznikA = 0;
	licznikB = 0;
	licznikC = 0;
	licznikD = 0;
	czyC = false;
	rygiel = new Object();
	watekA = new WatekA();
	watekB = new WatekB();
}

public void run()
{
	try
	{
		synchronized(rygiel)
		{
			watekA.start();
			watekB.start();
			while(!this.isInterrupted())
			{
				while(watekA.licznik == watekB.licznik*2) 
				{
					watekA.wait();
					System.out.println("A czeka");
				}
				watekA.notify();
			}
		}
	}
	catch(InterruptedException e)
	{
		System.out.println("Watek: ktos przerwal mi prace!");
	}
}

}

class WatekA extends Thread
{
public int licznik = 0;
public void run()
{
try
{
while(!this.isInterrupted())
{
System.out.println("A");
licznik++;
sleep((int)(Math.random()*1000));
}
}
catch(InterruptedException e)
{
System.out.println("Watek A: ktos przerwal mi prace!");
}
}
}

class WatekB extends Thread
{
public int licznik = 0;

public void run()
{
	try
	{
		while(!this.isInterrupted())
		{
			System.out.println("B");
			licznik++;
			sleep((int)(Math.random()*1000));
		}
	}
	catch(InterruptedException e)
	{
		System.out.println("Watek B: ktos przerwal mi prace!");
	}
}

}
[/code]

Wyświetla mi się jednak błąd java.lang.IllegalMonitorStateException . Jak sobie z tym poradzić i czy w ogóle to jak podchodzę do problemu jest właściwe (schemat klas itp.)? Byłbym bardzo wdzięczny za wszelką pomoc, ponieważ próbuję rozgryźć ten program już bardzo długo i nie mogę sobie poradzić! :(

0

Użycie funkcji sleep i notify wymaga synchronizacji. Użyj :

synchronized(watek){
   watek.notify();
}

ps. popraw [code] na < code=java> (bez spacji)

0

public class Literki
{
    public synchronized static void main (String args[])
    {
            Watek watek = new Watek();
            watek.start();
    }
}

class Watek extends Thread
{
        private int licznikA;
        private int licznikB;
        private int licznikC;
        private int licznikD;
        private boolean czyC;
        private Object rygiel;
        WatekA watekA;
        WatekB watekB;
       
        public Watek()
        {
                licznikA = 0;
                licznikB = 0;
                licznikC = 0;
                licznikD = 0;
                czyC = false;
                rygiel = new Object();
                watekA = new WatekA();
                watekB = new WatekB();
        }
       
        public void run()
        {
                try
                {
                        synchronized(rygiel)
                        {
                                watekA.start();
                                watekB.start();
                                while(!this.isInterrupted())
                                {
                                        while(watekA.licznik == watekB.licznik*2)
                                        {
                                                watekA.wait();
                                                System.out.println("A czeka");
                                        }
                                        watekA.notify();
                                }
                        }
                }
                catch(InterruptedException e)
                {
                        System.out.println("Watek: ktos przerwal mi prace!");
                }
        }
}

class WatekA extends Thread
{
        public int licznik = 0;
        public void run()
        {
                try
                {
                        while(!this.isInterrupted())
                        {
                                System.out.println("A");
                                licznik++;
                                sleep((int)(Math.random()*1000));
                        }
                }
                catch(InterruptedException e)
                {
                        System.out.println("Watek A: ktos przerwal mi prace!");
                }
        }
}

class WatekB extends Thread
{
        public int licznik = 0;
       
        public void run()
        {
                try
                {
                        while(!this.isInterrupted())
                        {
                                System.out.println("B");
                                licznik++;
                                sleep((int)(Math.random()*1000));
                        }
                }
                catch(InterruptedException e)
                {
                        System.out.println("Watek B: ktos przerwal mi prace!");
                }
        }
}
</code=java>
0

Tak jest zdecydowanie lepiej.
synchronizacji podlegają te obiekty na których wykonywane są metody sleep i notify

public class Literki {
	public synchronized static void main(String args[]) {
		Watek watek = new Watek();
		watek.start();
	}
}

class Watek extends Thread {
	private int licznikA;
	private int licznikB;
	private int licznikC;
	private int licznikD;
	private boolean czyC;
	private Object rygiel;
	WatekA watekA;
	WatekB watekB;

	public Watek() {
		licznikA = 0;
		licznikB = 0;
		licznikC = 0;
		licznikD = 0;
		czyC = false;
		rygiel = new Object();
		watekA = new WatekA();
		watekB = new WatekB();
	}

	public void run() {
		watekA.start();
		watekB.start();
		while (!this.isInterrupted()) {
			while (watekA.licznik == watekB.licznik * 2) {
				synchronized (watekA) {
					watekA.notify();
				}
				System.out.println("A czeka");
			}
			synchronized (watekA) {
				watekA.notify();
			}
		}
	}
}

class WatekA extends Thread {
	public int licznik = 0;

	public void run() {
		try {
			while (!this.isInterrupted()) {
				System.out.println("A");
				licznik++;
				synchronized (this) {
					sleep((int) (Math.random() * 1000));
				}

			}
		} catch (InterruptedException e) {
			System.out.println("Watek A: ktos przerwal mi prace!");
		}
	}
}

class WatekB extends Thread {
	public int licznik = 0;

	public void run() {
		try {
			while (!this.isInterrupted()) {
				System.out.println("B");
				licznik++;
				synchronized (this) {
					sleep((int) (Math.random() * 1000));
				}
			}
		} catch (InterruptedException e) {
			System.out.println("Watek B: ktos przerwal mi prace!");
		}
	}
}

A i jeszcze jedno nie twórz klasy na każdy wątek osobno. Lepiej w konstruktorze przekazywać obiektowi nazwę (A, B, C, D) lub wykorzystać API klasy Thread i nadawać wątkom nazwy.

0

Tak mi się nudziło, więc sobie rozwiązałem ;-)

public class Abcd {
    public static void main(String[] args) {
        Counter c = new Counter();
        for (ThreadType tt : ThreadType.values()) {
            new MyThread(tt, c).start();
        }
    }
}
public enum ThreadType {
    A, B, C, D;
    
    @Override
    public String toString() {
        switch (this) {
            case A: return "A";
            case B: return "B";
            case C: return "C";
            case D: return "D";
            default: return null;
        }
    }
}
public class Counter {
    private int counts[] = new int[ThreadType.values().length];
    
    public synchronized int getCount(ThreadType tt) {
        return counts[tt.ordinal()];
    }
    
    public synchronized void addCount(ThreadType tt) {
        counts[tt.ordinal()]++;
        System.out.println(tt);
    }
    
    public synchronized boolean canA() {
        return counts[ThreadType.A.ordinal()] < counts[ThreadType.C.ordinal()]
            && counts[ThreadType.A.ordinal()] < counts[ThreadType.D.ordinal()]
            && counts[ThreadType.A.ordinal()] < counts[ThreadType.B.ordinal()] * 2;
    }
    
    public synchronized boolean canB() {
        return counts[ThreadType.B.ordinal()] < counts[ThreadType.C.ordinal()]
            && counts[ThreadType.B.ordinal()] < counts[ThreadType.D.ordinal()];
    }
}
import java.util.Random;

public class MyThread extends Thread {
    private static final Random RANDOM = new Random();
    
    private static final int MAX_RANDOM = 1000;
    
    private ThreadType threadType;
    
    private Counter counter;
    
    private int lastDCount = -1;
    
    private boolean running;
    
    public MyThread(ThreadType tt, Counter c) {
        super(tt.toString());
        threadType = tt;
        counter = c;
    }
    
    @Override
    public void run() {
        running = true;
        while (running) {
            try {
                switch (threadType) {
                    case A:
                        synchronized (counter) {
                            while (!counter.canA()) {
                                counter.wait();
                            }
                            counter.addCount(threadType);
                            counter.notifyAll();
                        }
                    break;
                    case B:
                        synchronized (counter) {
                            while (!counter.canB()) {
                                counter.wait();
                            }
                            counter.addCount(threadType);
                            counter.notifyAll();
                        }
                    break;
                    case C:
                        synchronized (counter) {
                            while (lastDCount >= 0 && lastDCount == counter.getCount(ThreadType.D)) {
                                counter.wait();
                            }
                            counter.addCount(threadType);
                            lastDCount = counter.getCount(ThreadType.D);
                            counter.notifyAll();
                        }
                    break;
                    case D:
                        synchronized (counter) {
                            counter.addCount(threadType);
                            counter.notifyAll();
                        }
                }
                Thread.sleep(RANDOM.nextInt(MAX_RANDOM) + 1);
            } catch (InterruptedException e) {
                System.err.println("Thread " + getName() + ": Interrupted by another thread!");
            }
        }
    }
}

BTW. Synchronizacja na sleepie, to najgłupsza rzecz jaką można zrobić! NIGDY nie synchronizuj na sleepie - blokujesz w ten sposób zajęty przez wątek zasób! [diabel]

0

koledze rogatemu z pewnoscia chodzilo o pare metod wait i notify/notifyAll
co do synchronizacji na sleep - to faktycznie nie za dobre jest, poniewaz watek dostaje locka, i pierwsze co robi to spi przez jakis czas, blokujac inne watki przed uzyskaniem locka, no i nic sie przez ten czas nie dzieje
sleep nie musi byc w bloku synchronized (ponownie - wait i notify/notifyAll musza byc)

0

Myślę, że się mylisz, ponieważ kod, który napisał temu przeczy, a świadczy o niewielkim pojęciu o wielowątkowości.
Nie chodzi o to, że sleep nie musi być synchronizowany, tylko że nie powinien być synchronizowany. Synchronizacja na sleepie nie ma najmniejszego sensu. Wątek blokuje zasób nic z nim nie robiąc. Nie przychodzi mi teraz do głowy żadne racjonalne użycie synchronizowanego sleepa.

0

nie czytalem kody to fakt, ale o sleepie napisalem mniej wiecej to samo co ty, a osadzalem tylko na podstawie tego co napisal nad kodem
ogolnie koles sie pomylil i juz, nie sadze ze mozna napisac ze nie ma pojecia o wielowatkowosci

0

Rzeczywiście pisząc kod troszkę przekombinowałem. Raczej z pośpiechu i niedopatrzenia niż z chęci wprowadzenia kogokolwiek w błąd.

edit:

Nie przychodzi mi teraz do głowy żadne racjonalne użycie synchronizowanego sleepa.

W problemie "Śpiącego Fryzjera" wątek aktualnie obsługiwanego klienta powinien blokować zasób Fryzjer tak by żaden inny wątek nie mógł się do niego dostać i "wepchnąć się" na fotel. W takim wypadku warto synchronizować sleep, by fryzjer rzeczywiście obsługiwał jednego klienta na raz.

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