Wątek to program sekwencyjny, który może być wykonywany współbieżnie z innymi wątkami.
run()
start()
run()
Uwaga - wywołanie
run()
wykona funkcję, ale nie jako osobny wątek.
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", }; }
Zadeklaruj zagnieżdżoną klasę Downolader
. [Jeżeli wolisz, możesz utworzyć klasę zewnętrzną]
run()
i implementuje interfejs Runnable
można wiec ją uruchomić jako wątek lub bezpośredniostatic 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); } }
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); }
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?
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
DownloadExample
zadeklaruj statyczną zmienną static int count = 0;
Downloader
po zakończeniu zwiększ count o jedenwhile(count!=toDownload.length){ // wait... Thread.yield(); }
Zmień typ danych count
na AtomicInteger i podmień wszystkie operacje. To bezpieczniejsze (i zalecane) rozwiązanie.
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)
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.