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.
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()); } }
Gdyby ktoś potrzebował inspiracji - można zajrzeć na kod rysujący tarczę zegara w JavaScript.
Kontekst graficzny ctx
w JavaScript jest analogiem Grapics2D w bibliotece Swing. Transformacje afiniczne mają podobną postać…
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 = { "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 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?
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.