====== 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// -- {{ :java:w7-java-wyjatki.pdf | 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 [[https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicInteger.html|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 - [[https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html|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.