public void run()

Transkrypt

public void run()
Programowanie
sieciowe
mgr Marcin Raniszewski
mgr inŜ. Paweł Kośla
Łódź, 2009
Wykład 6: Wielowątkowość,
operacje na plikach,
kolekcje
1
Plan wykładu
Wątki (klasa Thread i interfejs Runnable)
Synchronizacja
„śycie” wątków
Odczyt i zapis do plików
Typowe kolekcje
2
Wielowątkowość
Proces to egzemplarz wykonywanego programu (inaczej: program w
trakcie wykonywania).
KaŜdemu procesowi system operacyjny przydziela zasoby, takie jak:
procesor,
pamięć (przestrzeń adresowa),
dostęp do urządzeń wejścia-wyjścia oraz plików.
Wątek (Thread) to jednostka wykonawcza w obrębie jednego procesu,
będąca kolejnym ciągiem instrukcji wykonywanym w obrębie tych samych
danych (procesu).
Wątki mogą korzystać z przestrzeni adresowej procesu, ale posiadają
własne kopie zmiennych lokalnych.
3
Wielowątkowość
W Javie istnieją dwa sposoby korzystania z wątków:
- tworzenie klas pochodnych klasy Thread
- implementacja interfejsu Runnable
Klasa, której obiekty mają działać jako wątki musi zawierać metodę:
public void run()
class MyThread extends Thread {
public void run() { ... }
}
...
MyThread watek = new MyThread();
lub:
class MyThread implements Runnable {
public void run() { ... }
}
...
Thread watek = new Thread(new MyThread());
4
Wielowątkowość
Wątki uruchamia się metodą watek.start().
Wówczas, wykonywane są instrukcje zaimplementowane w metodzie
run() (niezaleŜnie od instrukcji głównego programu).
Wątek trwa do zakończenia metody run().
Mimo, Ŝe istnieje takŜe metoda stop(), to nie naleŜy jej uŜywać do
wymuszania zakończenia wątku – lepiej sprawdzać warunek logiczny
wewnątrz run().
5
Wielowątkowość - synchronizacja
synchronized – zabezpiecza metody lub obiekty przed jednoczesnym
dostępem przez wiele wątków.
Aby wskazać, Ŝe pewne linie kodu powinny być wykonywane razem, naleŜy
je umieścić w bloku synchronized, który przeprowadza synchronizację
na pewnym obiekcie, którego te linie kodu dotyczą.
Jeśli metoda oznaczona jako synchronized jest aktualnie wykonywana,
to wszystkie kolejne jednoczesne próby jej realizacji zostaną wstrzymane
aŜ do czasu zakończenia pierwszego wywołania.
public synchronized void add(Object obj) {
...
}
synchronized (System.out) {
//Pewne operacje na obiekcie System.out
//Podczas wykonywania tego bloku przez pewien wątek załoŜona zostaje
//blokada na dostęp do obiektu System.out
//Jeśli pewien inny wątek chce skorzystać z obiektu System.out
//zostaje zablokowany do czasu zdjęcia blokady z tego obiektu
}
6
Wielowątkowość - synchronizacja
Synchronizacja bloku jest tylko częściową blokadą obiektu, na którym jest
załoŜona.
Inne metody mogą korzystać z synchronizowanego obiektu o ile nie usiłują
synchronizować się na tym obiekcie.
Nie zawsze synchronizacja jest najlepszym rozwiązaniem i często warto
pokusić się o rozwiązania alternatywne:
uŜywanie zmiennych lokalnych (kaŜdy wątek działa na własnym
zestawie zmiennych lokalnych),
uŜywanie metod działających na argumentach typów prostych
(przekazywanych przez wartość) lub bezargumentowych,
tworzenie klas niezmiennych (deklaracja wszystkich pól jako
prywatnych, nie pisanie metod modyfikujących pola klasy).
7
Wielowątkowość - priorytety
KaŜdy wątek ma priorytet – w Javie jest to liczba całkowita z zakresu od 1
do 10.
Wątek o priorytecie 10 będzie wykonywany w pierwszej kolejności, wątek
o priorytecie 1 – w ostatniej.
Gdy gotowych do wykonania jest wiele wątków, maszyna wirtualna Javy
uruchamia tylko ten o najwyŜszym priorytecie.
Domyślnym priorytetem dla tworzonych wątków jest 5.
Priorytety wątków przydatne gdy chcemy niektórym z nich przydzielić
więcej czasu procesora (wątki do komunikacji z uŜytkownikami – krótki
czas odpowiedzi), a niektórym mniej (wątki dokonujące obliczeń, wątki
długotrwałe – brak wymagania co do prędkości realizacji).
Pola i metody klasy Thread związane z priorytetami:
public static final int MIN_PRIORITY = 1
public static final int NORM_PRIORITY = 5
public static final int MAX_PRIORITY = 10
public final void setPriority(int newPriority)
public final int getPriority()
8
Wielowątkowość - wywłaszczenie
Wszystkie maszyny wirtualne Javy do zarządzania wątkami o róŜnych
priorytetach uŜywają programu z wywłaszczeniowym harmonogramem
wątków (wątek o wyŜszym priorytecie wywłaszcza <przerywa działanie>
wątek o niŜszym priorytecie).
W przypadku działania wielu wątków o równych priorytetach:
program wywłaszczający w sposób przypadkowy zatrzymuje
działanie wątków i przydziela czas procesora innym wątkom,
WaŜne jest aby upewnić się, Ŝe wszystkie wątki zatrzymują się
periodycznie, po to by inne wątki miały okazję zadziałać.
9
Wielowątkowość - wywłaszczenie
Wątek moŜe się zatrzymać lub ustąpić miejsca innym wątkom poprzez:
zablokowanie wejścia i wyjścia,
zablokowanie obiektu synchronizowanego,
rezygnację,
przejście w stan uśpienia,
połączenie z innym wątkiem,
czekanie na obiekcie,
skończenie działania,
wywłaszczenie przez inny wątek o wyŜszym priorytecie,
zatrzymanie.
10
Wielowątkowość - zablokowanie
Zablokowanie występuje wtedy gdy wątek musi się zatrzymać by poczekać
na zasób, którego nie posiada.
Najczęściej wątki blokują się na elementach wejścia/wyjścia systemu
(czekanie na przybycie danych z sieci).
Blokada występuje takŜe podczas próby dostępu do bloków lub metod
synchronizowanych, na których jest załoŜona blokada.
11
Wielowątkowość - rezygnacja
Wątek moŜe oddać sterowanie poprzez jawną rezygnację.
SłuŜy do tego statyczna metoda klasy Thread:
public static void yield()
Wątek wywołując tą metodę, sygnalizuje wirtualnej maszynie Javy, Ŝe
moŜe ona zacząć inny wątek, jeśli jest gotowy do uruchomienia.
Wątek podczas rezygnacji nie powinien być na niczym zsynchronizowany
(rezygnacja wątku nie zwalnia Ŝadnych blokad naleŜących do wątku).
12
Wielowątkowość – stan uśpienia
Stan uśpienia to forma rezygnacji z zasobów systemu na pewien ustalony
czas.
Wątek przechodzący w stan uśpienia podobnie jak podczas rezygnacji
zatrzymuje wszystkie swoje blokady, więc nie powinien on tego robić w
blokach zsynchronizowanych.
Odpowiednie metody z klasy Thread to:
public static void sleep(long milliseconds) throws
InterruptedException
public static void sleep(long milliseconds, int
nanoseconds) throws InterruptedException
Nie ma gwarancji, Ŝe wątek będzie „spał” dokładnie tyle, ile chcemy. Po
pierwsze nie wiadomo czy wirtualna maszyna Javy na danym systemie ma
moŜliwość odmierzenia czasu z dokładnością do nanosekund czy
milisekund. Po drugie po przebudzeniu wątku, moŜe on czekać w kolejce
na przydzielenie czasu procesora.
13
Wielowątkowość – stan uśpienia
MoŜliwe jest przedwczesne przebudzenie uśpionego wątku poprzez
wywołanie metody:
public void interrupt()
Wywołanie tej metody dla wątku ustawia status przerwania (interrupted
status) wątku na true.
Wywołanie tej metody w przypadku uśpionego wątku powoduje przerwanie
snu wątku i wyrzucenie wyjątku InterruptedException. Status
przerwania wątku nadal pozostaje ustawiony na false.
14
Wielowątkowość – stan uśpienia
public class Watek {
public static void main(String[] args) {
System.out.println("Start programu");
MyThread watek1 = new MyThread(1);
MyThread watek2 = new MyThread(2);
watek1.start();
watek2.start();
}
}
class MyThread extends Thread {
int nr;
MyThread(int i) {
nr=i;
}
Start
watek
watek
watek
watek
watek
watek
watek
watek
watek
watek
watek
watek
programu
1: 0
2: 0
1: 1
2: 1
1: 2
2: 2
1: 3
2: 3
1: 4
2: 4
1: 5
2: 5
public void run() {
for(int i=0 ; i<10; i++){
System.out.println("watek " + nr + ": " + i);
try {
this.sleep(100);
}
catch(InterruptedException ie) {
System.out.println("Nastąpiło przerwanie snu wątku " + nr);
}
}
}
}
15
Wielowątkowość – łączenie z innym
wątkiem
Łączenie wątków jest przydatne gdy jednemu wątkowi potrzebny jest
wynik pracy drugiego wątku.
Do łączenia wykorzystuje się następujące metody klasy Thread:
public final void join() throws InterruptedException
public final void join(long milliseconds) throws
InterruptedException
public final void join(long milliseconds, int
nanoseconds) throws InterruptedException
16
Wielowątkowość – łączenie z innym
wątkiem
public class Watek {
public static void main(String[] args) {
try{
System.out.println("start programu");
MyThread watek = new MyThread();
watek.start();
System.out.println("Kolejna instrukcja");
watek.join();
System.out.println("Nastepna instrukcja");
}
catch(InterruptedException e)
{}
}
}
class MyThread extends Thread
{
public void run()
{
for(int i=0 ; i<10; i++){
System.out.println("watek: "
}
}
}
start programu
Kolejna instrukcja
watek: 0
watek: 1
watek: 2
watek: 3
watek: 4
watek: 5
watek: 6
watek: 7
watek: 8
watek: 9
Nastepna instrukcja
+ i);
17
Wielowątkowość – czekanie na
obiekcie
MoŜliwe jest czekanie na pewnym obiekcie przez dany wątek.
Do czekania na obiekcie moŜna uŜyć metod klasy Object:
public final void wait() throws InterruptedException
public final void wait(long milliseconds) throws
InterruptedException
public final void wait(long milliseconds, int
nanoseconds) throws InterruptedException
18
Wielowątkowość – czekanie na
obiekcie
Wątek czeka na obiekcie dopóki:
nie upłynie czas oczekiwania,
wątek nie zostanie przerwany,
obiekt nie zostanie powiadomiony.
19
Wielowątkowość – czekanie na
obiekcie
Powiadomienie obiektu następuje wtedy, gdy jakiś inny wątek wywoła na
nim metodę klasy Object:
public final void notify()
public final void notifyAll()
Metoda notify() w dość losowy sposób wybiera jeden z listy wątków
czekających na obiekcie i go budzi.
Metoda notifyAll() budzi wszystkie wątki czekające na danym
obiekcie.
20
Wielowątkowość – zatrzymanie
Gdy metoda run() kończy swoje działanie, wątek umiera.
NaleŜy pamiętać by nie przesadzać z tworzeniem wątków, nie tworzyć ich
bez potrzeby. Jeśli działanie funkcji run() jest bardzo proste, krótkie i
moŜe je zrealizować wątek główny programu to lepiej nie tworzyć wątku.
Utworzenie wątku wiąŜe się z pewnym narzutem czasu działania, więc jest
wysoko prawdopodobne, Ŝe wykonanie tych samych operacji w głównym
wątku będzie szybsze niŜ w oddzielnym wątku.
21
import java.net.*;
import java.io.*;
import java.util.*;
public class Server {
public static void main(String[] args) {
Server me = new Server();
me.echo("192.168.0.2");
}
public void echo(String host){
try{
InetAddress my = InetAddress.getByName("192.168.0.2");
ServerSocket serw = new ServerSocket(1090);
while(true){
Socket sock = serw.accept();
new Kl(sock).start();
}
} catch(IOException e){
e.printStackTrace();
} finally {
(...)
}
}
}
class Kl extends Thread {
Socket klient;
Kl(Socket sock){
this.klient = sock;
}
public void run(){
try{
ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(klient.getOutputStream()));
out.writeDouble(23.54); out.flush();
klient.close();
}
catch(IOException e){
e.printStackTrace();
} finally {
(...)
}
22
}
}
Klasa File
Klasa File reprezentuje nazwę konkretnego pliku albo nazwę zbioru
plików w katalogu.
Najczęściej uŜywanym konstruktorem klasy File jest:
public File(String pathname) throws NullPointerException
gdzie pathname to ścieŜka dostępu do pliku lub katalogu. Konstruktor
wyrzuci wyjątek NullPointerException jeśli pathname jest null-em.
Kolejnymi konstruktorami są:
public File(String parent, String child) throws
NullPointerException
public File(File parent, String child) throws
NullPointerException
tworzą one ścieŜkę do pliku lub katalogu na podstawie istniejącej ścieŜki
parent i nowej ścieŜki child (konkatenacja). Jeśli parent jest null-em –
nie jest brany pod uwagę, jeśli child jest null-em - wyrzucany jest wyjątek
23
NullPointerException.
Klasa File
Istnieje wiele uŜytecznym metod klasy File, oto niektóre z nich:
public
public
public
public
public
public
public
public
public
public
public
public
public
boolean canRead()
boolean canWrite()
boolean createNewFile()
boolean delete()
boolean exists()
String getPath()
boolean isDirectory()
boolean isFile()
boolean isHidden()
long lastModified()
long length()
String[] list()
boolean mkdir()
24
Odczyt/zapis z/do pliku
W Javie odczyt i zapis do plików realizowany jest na zasadzie strumieni.
Po otwarciu pliku do odczytu, cała jego zawartość jest dostępna jako
strumień. Odczytując z niego dane, „wyciągamy” je bajt po bajcie ze
strumienia.
Jeśli chcemy pisać do pliku to teŜ konstruujemy odpowiedni strumień i
piszemy na nim, a dane trafiają do pliku.
Standardowo czytanie i zapis do pliku odbywa się bajt po bajcie. Do
odczytu pojedynczych bajtów słuŜy klasa FileInputStream, a do zapisu
FileOutputStream.
25
Odczyt z pliku
FileInputStream fis = null;
try {
fis = new FileInputStream("pliktestowy.txt");
int bajt = 0;
while((bajt = fis.read()) != -1){
System.out.print((char)bajt);
} catch (IOException ex){
System.out.println("Błąd przy odczycie danych: " + ex);
} finally {
(...)
}
26
Odczyt z pliku
Java, oprócz czytania bajt po bajcie, oferuje takŜe moŜliwość odczytu całej
linii do obiektu typu String. MoŜliwe jest to dzięki tzw. strumieniom
buforowanym. Najpierw trzeba utworzyć obiekt FileReader, a następnie
trzeba stworzyć obiekt BufferedReader, podając mu obiekt
FileReader jako argument:
String linia = null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("plik_testowy.txt");
while((linia = br.readLine()) != null){
System.out.println(linia);
}
} catch (IOException ex){
System.out.println("Błąd przy operacji na pliku: "+ex);
} finally {
(...)
}
27
Zapis do pliku
FileOutputStream fos = null;
try{
String str = "Próba zapisu";
fos = new FileOutputStream("plik_zapis.txt");
for(int i = 0; i < str.length(); i++){
fos.write((int)str.charAt(i));
}
} catch(IOException ex) {
System.out.println("Błąd operacji na pliku: " + ex);
} finally {
(...)
}
28
Zapis do pliku
Podobnie, aby móc buforować zapis do pliku moŜna posłuŜyć się klasami
PrintWriter, BufferedWriter i FileWriter:
PrintWriter pw = null;
try{
String str = "Próba zapisu";
pw = new PrintWriter(new BufferedWriter(new FileWriter("plik_zapis.txt")));
pw.write(str);
pw.flush();
} catch(IOException ex) {
System.out.println("Błąd operacji na pliku: " + ex);
} finally {
(...)
}
29
Kolekcje
Kolekcje to implementacje struktur danych, słuŜących do
przechowywania, przeglądania i zarządzania grupami obiektów
Dobór odpowiedniej kolekcji zaleŜy od rozwiązywanego problemu:
Czy będziemy chcieli przeszukiwać zbiór składający się z
tysięcy czy nawet milionów elementów?
Czy niezbędne będzie uporządkowanie tych elementów,
moŜliwość łatwego wstawiania, usuwania elementów?
Czy elementy mają być szybko wyszukiwane?
Kolekcje w Javie rozdzielone są na:
interfejsy – definiują abstrakcyjne właściwości i operacje
kolekcji, w oderwaniu od konkretnych implementacji
implementacje – klasy będące implementacjami konkretnych
interfejsów
30
Interfejs Collection
Interfejs Collection reprezentuje dowolną grupę obiektów.
public interface Collection {
boolean add (Object o);
boolean contains(Object o);
boolean isEmpty();
boolean remove(Object o);
int size();
Iterator iterator();
...
}
Collection
Set
Object first();
Object last();
...
List
boolean add(int index, Object o);
Object remove(int index);
Object set(int index, Object o);
...
SortedSet
31
Interfejsy Set, SortedSet,
List
Interfejs Set implementują zbiory, czyli struktury danych, nie
zawierające dublujących się elementów. Bardziej formalnie zbiór nie
moŜe zawierać dwóch obiektów p1 i p2, dla których
p1.equals(p2) zwróci wartość true. Zbiór moŜe zawierać co
najwyŜej jeden obiekt null. Klasy implementujący ten interfejs to:
HashSet, LinkedHashSet, TreeSet
Interfejs SortedSet jest podinterfejsem interfejsu Set i gwarantuje
poruszanie się po kolekcji w sposób uporządkowany. Obiekty
przechowywane w kolekcjach implementujących ten interfejs
powinny implementować interfejs Comparable. Klasa implementująca
ten interfejs to: TreeSet
Interfejs List implementują listy, czyli struktury danych, w których
elementy mają z góry ustalone miejsce. Elementy mogą się
dublować. Klasy implementujące ten interfejs to: ArrayList,
LinkedList
32
Interfejs Map
Interfejs Map reprezentuje grupę odwzorowań klucz i wartość. Klucze nie
mogą być zdublowane. Klasy które implementują ten interfejs to:
HashMap, Hashtable, TreeMap.
public interface Map {
boolean containsKey (Object key);
boolean containsValue(Object value);
Object get(Object key);
boolean isEmpty();
Object put(Object key, Object value)
Object remove(Object key);
int size();
Collection values();
...
}
Map
SortedMap
Object firstKey();
Object lastKey();
...
33
Interfejs SortedMap
Interfejs SortedMap reprezentuje mapy, które gwarantują
sortowanie kluczy, według porządku wprowadzonego poprzez
implementacje interfejsu Comparable dla obiektu kluczy lub poprzez
podanie klasy komparatora przy tworzeniu mapy (który będzie
uŜywany do sortowania kluczy). Klasa komparatora to klasa
implementująca interfejs Comparator. Klasa implementująca
interfejs SortedMap to TreeMap
34
Interfejs Iterator
UmoŜliwia poruszanie się po dowolnej klasie implementującej
interfejs Collection
public interface Iterator {
boolean hasNext();
Object next();
void remove();
}
•
•
•
Powtarzając wywołanie metody next() dla iteratora moŜemy
odwiedzić kolejne obiekty kolekcji.
Jeśli dotrzemy w ten sposób do końca kolekcji, to wywołanie metody
next() spowoduje wyrzucenie wyjątku
NoSuchElementException. Dlatego teŜ wywołanie metody
next() nalezy poprzedzić wywołaniem metody hasNext()
Kolejność odwiedzania elementów zaleŜy od typu kolekcji. Jeśli
odwiedzamy elementy kolekcji ArrayList, to iterator rozpoczyna
działanie od elementu o indeksie 0 i następnie zwiększa wartość
indeksu o 1. Dla kolekcji HashSet elementy będą odwiedzane w
35
przypadkowej kolejności
Interfejs Iterator
MoŜemy być pewni, Ŝe uŜywając iteratora, odwiedzimy wszystkie
elementy kolekcji, ale nie wolno nam czynić załoŜeń odnośnie ich
uporządkowania
Iteratory w Javie powinniśmy sobie wyobraŜać jako znaczniki
ustawione zawsze pomiędzy elementami kolekcji (jeśli powołujemy
iterator to jest on ustawiony przed elementami kolekcji). Wywołanie
metody next() powoduje, Ŝe iterator przesuwa się ponad kolejnym
elementem kolekcji i zwraca referencję do niego
36
Wykaz najwaŜniejszych
implemenatcji kolekcji
Typ kolekcji
Opis
ArrayList
sekwencja indeksowana o zmiennych rozmiarach
(odpowiednik tablicy)
LinkedList
sekwencja uporządkowana umoŜliwiająca efektywne
wstawianie i usuwanie elementów (odpowiednik listy)
HashSet
zbiór nieuporządkowany (nie dopuszcza duplikatów)
TreeSet
zbiór uporządkowany
LinkedHashSet
zbiór zapamiętujący kolejność wstawiania elementów
HashMap
mapa (asocjacje klucz/wartość)
TreeMap
mapa o kluczach uporządkowanych
LinkedHashMap
mapa zapamiętująca porządek, w którym
umieszczane były asocjacje
37
Przykłady uŜycia ArrayList
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
public class TestArrayList {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Point(1,1));
list.add(new Point(1,2));
list.add(new Point(1,3));
list.add(1, new Point(2,4));
Iterator i = list.iterator();
while (i.hasNext()) {
System.out.println((Point)i.next());
}
list.remove(3);
list.set(1, new Point(3,3));
System.out.println("==po modyfikacjach==");
i = list.iterator();
while (i.hasNext()) {
System.out.println((Point)i.next());
}
}
}
java.awt.Point[x=1,y=1]
java.awt.Point[x=2,y=4]
java.awt.Point[x=1,y=2]
java.awt.Point[x=1,y=3]
==po modyfikacjach==
java.awt.Point[x=1,y=1]
java.awt.Point[x=3,y=3]
java.awt.Point[x=1,y=2]
38
Przykłady uŜycia HashMap
import
import
import
import
java.util.HashMap;
java.util.Iterator;
java.util.Map;
java.util.Set;
public class Test2{
public static void main(String[] args) {
HashMap map = new HashMap();
map.put(new Integer(1), "Marcin");
map.put(new Integer(2), "Ada");
map.put(new Integer(4), "Weronika");
map.put(new Integer(3), "Krzysiek");
Set set = map.entrySet();
Iterator i = set.iterator();
while (i.hasNext()) {
Map.Entry m = (Map.Entry)i.next();
System.out.println((Integer)m.getKey()
}
map.remove(new Integer(2));
if (!map.containsKey(new Integer(5))) {
map.put(new Integer(5), "Pucio");
} else {
System.out.println("Klucz juz istnieje
}
System.out.println("==po modyfikacjach==");
i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry m = (Map.Entry)i.next();
System.out.println((Integer)m.getKey()
}
if (map.containsKey(new Integer(1))) {
System.out.println((String)map.get(new
}
}
}
2 -> Ada
4 -> Weronika
1 -> Marcin
3 -> Krzysiek
==po modyfikacjach==
4 -> Weronika
1 -> Marcin
3 -> Krzysiek
5 -> Pucio
Marcin
+ " -> " + (String)m.getValue());
w mapie");
+ " -> " + (String)m.getValue());
Integer(1)));
39

Podobne dokumenty