java 2 wątki równolegle

0

Cześć! Chciałbym wykorzystać fakt, że mam procesor dwurdzeniowy i na początek napisać program tworzący dwa wątki, które w odpowiednim momencie zaczną działać równolegle. Wymyśliłem na początek taki standardowy przykład żeby każdy element wektora pomnożyć przez jakąś liczbę. Między elementami wektora nie będzie więc żadnych zależności. Rozmiar wektora można podzielić przez 2 i wtedy 1 wątek mnożyłby elementy od 0 do rozmiar / 2, a drugi od rozmiar / 2 do rozmiar. Na razie mam taki pomysł aby napisać dodatkową klasę implementującą interfejs Runnable, utworzyć 2 obiekty tej klasy i wykorzystać je do utworzenia 2 wątków. Nie wiem jednak w jaki sposób metoda run klasy implementującej interfejs Runnable mógłby modyfikować obiekt, na którym ma operować zapisany w klasie głównej w której odpalam wątki ? Może powinien użyć innych mechanizmów ? Proszę o wskazówki.

0

Na przykład:

class MojRunnable implements Runnable {
  private Vector v;
  private int indeksPocz;
  private int indeksKon;
  public MojRunnable(Vector vecDoPomnozenia, int indeksPocz, int indeksKon) {
    v = vecDoPomnozenia;
    this.indeksPocz = indeksPocz;
    this.indeksKon = indeksKon;
  }

  @Override
  public void run() {
    for (int i = indeksPocz; i < indeksKon; i++) {
      v.set(i, v.get(i)*2);  // pewnie nie ma metody set(), ale chodzi mi o idee 
    }
  }
}

W kodzie oczywiscie nalezy uzyc genericsow, czyli Vector<Integer> albo sparametryzowac klase MojRunnable i tym samym sparametryzowac Vector.
Mam nadzieje, ze idea jest jasna ;)

0

Ok, dla uproszczenia wektorem niech będzie zwykła tablica float. A na końcu metody run dodajmy dodatkowo wydruk "Finished" :

 
public class MyRunnable implements Runnable {
    
    private int startIndex;
    private int endIndex;
    private float[] tab;

    public MyRunnable(int startIndex, int endIndex, float[] tab)
    {
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        this.tab = tab;
    }
    
    @Override
    public void run() 
    {
        for(int i = startIndex; i < endIndex; i++)
        {
            tab[i] = i * 2;
        }
        
        System.out.println("Finished");
    }
}

Wtedy mając już tą klasę MyRunnable mogą ją przetestować tak:

 
public class Test {

    public static void main(String[] args) {
        
        int size = 10;
        int n_threads = 2;
        float tab[] = new float[size];
        
        for(int i = 0; i < size; i++)
        {
            tab[i] = i;
        }
        
        for(int i = 0; i < size; i++)
        {
            System.out.println(tab[i]);
        }
        
        Runnable r1 = new MyRunnable(0, size / n_threads, tab );
        Runnable r2 = new MyRunnable(size / n_threads, size, tab );
        
        Thread t1 = new Thread(r1);
        Thread t2 = new Thread(r2);
        
        t1.start();
        t2.start();
        
        for(int i = 0; i < size; i++)
        {
            System.out.println(tab[i]);
        }
   }
    
}

Dostają taki wydruk: 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, Finished, 0.0, 2.0, 4.0, 6.0, 8.0, 5.0, 6.0, 7.0, 8.0, 9.0, Finished

Jeśli zakomentuję pierwszą pętlę wyświetlającą zawartość tablicy przed utworzeniem i uruchomieniem wątków dostanę np taki wydruk: 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0, Finished, Finished

Jeśli znowu odkomentuję pierwszą pętlę wyświetlającą zawartość tablicy wydruk jest taki: 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 0.0, 2.0, 4.0, 6.0, 8.0, 5.0, 6.0, 7.0, 8.0, 9.0,Finished,Finished

Coś chyba robię źle...

0
  1. Vector jest wewnętrznie synchronizowany. Tablica nie jest. Dwa różne wątki, które odpalasz dostają dostęp do obliczanych elementów poprzez kod nie przeznaczony do pracy wielowątkowej.
  2. Dlaczego uważasz, że zarządca wątków w systemie operacyjnym (a java używa obecnie natywnego) na pewno przydzieli dwa tak krótkie wątki dwóm różnym procesorom/rdzeniom? A jeżeli nie przydzieli (nie masz na to dosłownie żadnego wpływu), to założenie, iż wywłaszczanie wątków będzie tak znakomite, że po każdej iteracji metody run kontekst będzie przełączany do drugiego z wątków roboczych jest niezwykle ryzykowne (dokładniej błędne).
  3. Dlaczego uważasz, że t1 i t2 zakończą się zanim JVM dojdzie do pętli for, która występuje w czasie tuż za odpaleniem tych wątków. To wątki muszą Cię poinformować, że zakończyły swoje działanie aby można było wykonać kod korzystający z efektów obliczeń. W tym wypadku najłatwiej to uzyskać wywołując dla obu wątków metodę join() po obu wywołaniach start().
    Po powrocie z wywołanie drugiej metody start następną instrukcją w tym wątku jest for. Co wg Ciebie jest krótsze: przelecenie wszystkich iteracji pętli w run() czy przejście do uruchomienia trzeciej pętli for w main()?

Jeżeli chcesz robić kod współbieżny, to musisz porzucić większość pewników jakie są oczywiste dla kodu jednowątkowego.
Również metoda print wywoływana w wątkach (np. jako monitorowanie stanów pośrednich) nie gwarantuje w każdych warunkach, że to co zobaczysz na ekranie będzie zgodne z rzeczywistą kolejnością wywoływania takiej metody w wątkach.

Weź też pod uwagę, że kod który jest powyżej pracuje od pewnego miejsca w trzech wątkach, a nie dwóch.

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