Table of Contents
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 interfejsRunnable
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
Napisz kolejną wersję funkcji concurrentDownload()
lub skopiuj całą klasę i zmień jej nazwę na DownloadExampleVersion2
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 1acquire(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.