Wątek to program sekwencyjny, który może być wykonywany współbieżnie z innymi wątkami.
run()start()run()Zadeklaruj klasę Clock
public class Clock extends Thread{ @Override public void run() { } public static void main(String[] args) { new Clock().start(); } }
Następnie w metodzie run()
LocalTime time = LocalTime.now(); System.out.printf("%02d:%02d:%02d\n", time.getHour(), time.getMinute(), time.getSecond());
Prawdopodobnie ten sam czas drukuje się wielokrotnie.
Uśpij wątek na jedną sekundę (1000 milisekund) wprowadzając wywołanie metody sleep()
Zaimplementujemy zegar analogowy wyświetlający (i przesuwający wskazówki).
ClockWithGui dziedziczącej po JPanel. main() utworzona zostanie ramka, dodany do niej panel, itdpublic class ClockWithGui extends JPanel { LocalTime time = LocalTime.now(); public static void main(String[] args) { JFrame frame = new JFrame("Clock"); frame.setContentPane(new ClockWithGui()); frame.setSize(700, 700); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(true); frame.setVisible(true); } }
Kod umieszczamy w paintComponent()
Poniżej przykład rysowania cyfr na tarczy.
360/12 stopniy rosną w dół. Lokalizacja cyfr nie jest idealna, warto odjąć od x (przed przekształceniem) szerokość tekstu. Jeszcze lepiej zrealizowana metoda powinna odczytać wymiary tekstu dla danej czcionki.public void paintComponent(Graphics g){ Graphics2D g2d=(Graphics2D)g; g2d.translate(getWidth()/2,getHeight()/2); for(int i=1;i<13;i++){ AffineTransform at = new AffineTransform(); at.rotate(2*Math.PI/12*i); Point2D src = new Point2D.Float(0,-120); Point2D trg = new Point2D.Float(); at.transform(src,trg); g2d.drawString(Integer.toString(i),(int)trg.getX(),(int)trg.getY()); } }
Tak rysujemy jedną ze wskazówek (godzinową)…
AffineTransform saveAT = g2d.getTransform(); g2d.rotate(time.getHour()%12*2*Math.PI/12); g2d.drawLine(0,0,0,-100); g2d.setTransform(saveAT);
Możesz zmienić kształt (np. narysować wielobok) lub użyć pogrubienia g2d.setStroke(new BasicStroke(???, CAP_ROUND,JOIN_MITER))
Dorysuj samodzielnie…
W klasie ClockWithGui zadeklaruj klasę wewnętrzną będącą wątkiem.
class ClockThread extends Thread{ @Override public void run() { for(;;){ time = LocalTime.now(); System.out.printf("%02d:%02d:%02d\n",time.getHour(),time.getMinute(),time.getSecond()); //sleep(1000); repaint(); } } }
ClockThread możliwy jest dostęp do atrybutu time?repaint()? Jest to metoda wątku czy JPanel?Celem przykładu 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 = { "http://home.agh.edu.pl/pszwed/wyklad-c/01-jezyk-c-intro.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/02-jezyk-c-podstawy-skladni.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/03-jezyk-c-instrukcje.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/04-jezyk-c-funkcje.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/05-jezyk-c-deklaracje-typy.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/06-jezyk-c-wskazniki.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/07-jezyk-c-operatory.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/08-jezyk-c-lancuchy-znakow.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/09-jezyk-c-struktura-programow.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/10-jezyk-c-dynamiczna-alokacja-pamieci.pdf", "http://home.agh.edu.pl/~pszwed/wyklad-c/11-jezyk-c-biblioteka-we-wy.pdf", "http://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 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(); } 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... } 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?
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.