Wątki - pobieranie plików

Wątek to program sekwencyjny, który może być wykonywany współbieżnie z innymi wątkami.

  • Wykonywany kod umieszczamy w metodzie run()
  • Wątki uruchamiamy wywołując metodę start()
  • Wątki kończą działanie po wyjściu z metody run()

:!: Uwaga - wywołanie run() wykona funkcję, ale nie jako osobny wątek. :!:

Pobieranie plików

Celem zadania jest porównanie czasów sekwencyjnego i współbieżnego pobierania plików.

Całość zamkniemy w jednej klasie/pliku DownaloadExample. Ale oczywiście można kod rozdzielić.

public class DownloadExample {
 
// lista plików do pobrania
   static String [] toDownload = {
            "https://home.agh.edu.pl/~pszwed/wyklad-c/01-jezyk-c-intro.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/02-jezyk-c-podstawy-skladni.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/03-jezyk-c-instrukcje.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/04-jezyk-c-funkcje.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/05-jezyk-c-deklaracje-typy.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/06-jezyk-c-wskazniki.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/07-jezyk-c-operatory.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/08-jezyk-c-lancuchy-znakow.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/09-jezyk-c-struktura-programow.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/10-jezyk-c-dynamiczna-alokacja-pamieci.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/11-jezyk-c-biblioteka-we-wy.pdf",
            "https://home.agh.edu.pl/~pszwed/wyklad-c/preprocesor-make-funkcje-biblioteczne.pdf",
    };
 
}

Downloader

Zadeklaruj zagnieżdżoną klasę Downolader. [Jeżeli wolisz, możesz utworzyć klasę zewnętrzną]

  • Klasa ma metodę run() i implementuje interfejs Runnable można wiec ją uruchomić jako wątek lub bezpośrednio
  • Kopiowanie pliku - zapoznaj się z try with resource wykład o wyjątkach (pod koniec)
    static class Downloader implements Runnable{
        private final String url;
 
        Downloader(String url){
            this.url = url;
        }
 
        public void run(){
            String fileName = //nazwa pliku - wyodrębnij z z url
 
            try(InputStream in = new URL(url).openStream(); FileOutputStream out = new FileOutputStream(fileName) ){
                for(;;){
                    // czytaj znak z in
                    // jeśli <0 break
                    //zapisz znak do out
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("Done:"+fileName);
        }
 
    }

Pobieranie sekwencyjne

Zaimplementuj, uruchom, przetestuj metodę sequentialDownload

    static void sequentialDownload(){
        double t1 = System.nanoTime()/1e6;
        for(String url:toDownload){
            new Downloader(url).run();  // uwaga tu jest run()
        }
        double t2 = System.nanoTime()/1e6;
        System.out.printf(Locale.US,"t2-t1=%f\n",t2-t1);
    }

Pobieranie współbieżne v.1

    static void concurrentDownload(){
        double t1 = System.nanoTime()/1e6;
        for(String url:toDownload){
            // uruchom Downloader jako wątek... czyli wywołaj start()
        }
        double t2 = System.nanoTime()/1e6;
        System.out.printf(Locale.US,"t2-t1=%f\n",t2-t1);
    }

Czy podawany jest poprawny czas? Jeśli nie to dlaczego?

Pobieranie współbieżne v.2

Zrealizuj rozwiązanie polegające na zliczaniu pobranych plików

  • W klasie DownloadExample zadeklaruj statyczną zmienną static int count = 0;
  • W klasie Downloader po zakończeniu zwiększ count o jeden
  • Zaimplemntuj funkcję concurrentDownload2 z sekwencją oczekiwania na zakończenie pobierania plików
        while(count!=toDownload.length){
            // wait...
            Thread.yield();
        }

Pobieranie współbieżne v.2.5

Zmień typ danych count na AtomicInteger i podmień wszystkie operacje. To bezpieczniejsze (i zalecane) rozwiązanie.

Pobieranie współbieżne v.3

Zamiast aktywnego oczekiwania wątku na zakończenie operacji wprowadzimy mechanizm synchronizacji - semafor.

Semafor to zmienna całkowita (licznik), na której można wykonać dwie atomowe (=niepodzielne) operacje:

  • release() – zwiększa licznik o 1
  • acquire(int cnt)
    • zawiesza wątek w oczekiwaniu, aż licznik semafora będzie większy lub równy cnt,
    • następnie zmniejsza licznik o cnt i odblokowuje oczekujący wątek

Dodaj semfor do klasy DownloadExample

  static Semaphore sem = new Semaphore(0);
 

Po zakończeniu pobierania pliku w Downolader wywołaj sem.release (zwiększa licznik w semaforze o 1)

W funkcji concurrentDownload3() wywołaj sem.acquire() podając odpowiednią wartość. Wątek funkcji main() będzie czekał na dokończenie wywołania sem.acquire(), które nastąpi, kiedy wszystkie wątki Downloader zakończą działanie. Następnie odczytaj rzeczywiste czasy pobierania i porównaj z czasem sekwencyjnym.

pz1/watki-ladowanie-plikow.txt · Last modified: 2023/12/18 00:37 by pszwed
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0