Wzajemne przerywanie działania wątków

0

Witam, Javy się dopiero uczę więc wybaczcie jeśli będę plótł bzdrury :).
W wątku głównym programu uruchamiam 3 wątki poboczne które zajmują się szukaniem jakiejś z góry ustalonej liczby. Trzy działające współbieżnie wątki szukają tej liczby i jeśli jeden znajdzie liczbę to ją wypisuje i powinien przerwać działanie pozostałych.
Zrobiłem tak ale coś nie działa.
Opiszę mniej więcej najważniejsze punkty programu, które powinny sprawić, że wszystko będzie działać dobrze.

Wątki szukające liczby posiadają współdzieloną zmienna, która jest inicjowana wartością false w wątku głównym i jest przekazywana do każdego z wątków pobocznych w konstruktorze. Dlatego też we wszystkich wątkach mamy referencję do tego samego obiektu. Ale podobno wątki działają na kopiach zmiennych dlatego też trzeba albo zmienna współdzieloną zadeklarować z identyfikatorem volatile lub metodę run() uczynić synchronizowaną lub kod odwołujący się do zmiennej współdzielonej powinien znajdować się w bloku synchronizowany. Próbowałem zarówno volatile jak i synchronizacji. W metodzie run() wykonywana jest pętla coś w tym stylu:

for(int i = 0; i < gornyZakres; i++) {
  if(zmiennaWspoldzielona == true) {
    System.out.println("Inny wątek znalazł już liczbę");
    return;
  }
}

Wg. mnie powinno to działać... ale nie działa. Może ktoś podsunąć jakieś sugestie czego to wina?
Z góry dzięki. Pozdrawiam

0

Zmienne typu boolean są przekazywane przez wartość, nie przez referencję.
Podejrzewam, że stworzyłeś sobie w wątkach nowe zmienne, nadałeś im taką samą wartość jak zmienna rzekomo współdzielona, ale nie masz świadomości, że zmiana którejkolwiek z nich nie niesie za sobą zmiany reszty z nich.

0

Ok chyba doszedłem co było nie tak. Synchronizowanie nie działa na typach wbudowanych :P. Trzeba stworzyć obiekt własnej klasy który będziemy współdzielić. Dobrze mówię?

EDIT: a no w tym samym momencie :). Widać nie uważnie czytałem książkę. Dzięki za pomoc :)
Pozdrawiam

0

Witam, mam kolejny problem. Już kilka godzin nad tym siedzę i nic nie działa. Otóż jak wcześniej napisałem mam kilka wątków które szukają liczby. Chciałbym teraz zmierzyć czas od rozpoczęcia szukania do zakończenia.
Dlatego też w klasie szukającej liczbę umieściłem referencję do mojej własnej klasy Timer'a. Która zlicza kolejne milisekundy:

class Zarzadca {
    public boolean z;
    
    public Zarzadca() {
        this.z = false;
    }
}

public class Timer extends Thread {
    private Zarzadca running = null;
    
    public Timer() {
        this.running = new Zarzadca();
    }
    
    synchronized public void startTimer() {
        if(running.z == false) {
            System.out.println("Rozpoczynam pomiar czasu...");
            running.z = true;
            start();
        }
    }
    
    synchronized public void stopTimer() {
        running.z = false;
    }
    
    @Override
    synchronized public void run() {
        int i = 0;
        while(running.z) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException exc) { }
            i++;
        }
        System.out.println("Operacja generowania liczby trwała: "+ i +" milisekund");
    }
}

Akurat tutaj wszystkie metody są synchronizowane bo wszystkie operują na zmiennej running, która jest obiektem klasy Zarzadca. Teraz tak, w wątku głównym tworzę nowy obiekt klasy Timer i podaję go w konstruktorze każdego z wątków szukających liczbę. Oczywiście wszędzie gdzie jest tam odwołanie do obiektu klasy Timer używam synchronizacji:

class GeneratorLiczb extends Thread {
    private Zarzadca znalezione;
    private Timer timer = null;
    
    public GeneratorLiczb ([...], Timer timer) {
        [...]
        synchronized(this) {
            this.znalezione = znal;
            this.timer = timer;
        }
    }

W metodzie run() klasy GeneratorLiczb wywołuję metodę startTimer() a gdy szukana liczba zostaje znaleziona wywołuję stopTimer(). Oczywiście obydwa wywołania są synchronizowane. Niestety zdarza się czasami, że zostaną uruchomione dwa Timer'y. Już nie wiem co jest nie tak... Pomoże ktoś?

0

Ten Twój timer jest bez sensu i nie będzie dobrze czasu liczył.

Polecam metody System.currentTimeMillis() oraz System.nanoTime().

A upakowanie pojedynczego booleana do nowej klasy też moim zdaniem jest bardzo okrężnym i nietypowym sposobem na rozwiązanie Twojego problemu.

0

Pisanie "synchronized" w klasach z wątkami NIC nie daje

synchronized public void stopTimer() {
        running.z = false;
}

Jest tym samym co:

public void stopTimer() {
   synchronized(this){
        running.z = false;
  }
}

Oczywiście każdy wątek ma swoje własne "this". Dlatego każdy wątek synchronizuje się na innym obiekcie, co nic nie daje.

Tak naprawdę chodzi ci o:

public void stopTimer() {
   synchronized(Zarzadca.class){
        running.z = false;
  }
}

Jeżeli chcesz pisać programy współbieżne, to naprawdę polecam pakiet java.util.concurrent.
Na początku zapoznaj się z tymi trzema klasami:

java.util.concurrent.Semaphore
java.util.concurrent.ArrayBlockingQueue
java.util.concurrent.CountDownLatch

0

Z tego co czytałem to:

public void stopTimer() {
   synchronized(Zarzadca.class){
        running.z = false;
  }
}

Służy do synchronizacji pól statycznych. Jakoś nie bardzo już to rozumiem.
Dzięki za informację... poczytam o tym klasach.
Pozdrawiam

EDIT: aaaa już ok, kurde źle przeczytałem... przecież synchronizować trzeba obiekt współdzielony a nie obiekt klasy w którym ten obiekt współdzielony się znajduje... Przecież to oczywiste. Nie wiem jak ja to zrozumiałem... ale dziwne bo jak próbowałem to robić z użyciem volatile to też nie działało.

0

Tak naprawdę klasy Zarządca nie potrzebujesz, bo jest już coś takiego standardzie:

java.util.concurrent.atomic.AtomicBoolean

Czyli Boolean który ma metody set() i get() atomowe.

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