Programowanie obiektowe /Java/
Laboratorium nr 13
Uwaga !!! Wyniki uruchomionych programów mogą zależeć od sprzętu (ilość procesorów, rdzeni itp.), systemu operacyjnego i jego obciążenia, ilości wątków w programie (zmienna SIZE), itp.
1
Wątki
Interfejs Runnable1:
public void run() – metoda, która ma się wykonywać współbieżnie.
W celu uruchomienia nowego wątku należy w wybranej klasie zaimplementować interfejs Runnable. Na-stępnie jako parametr konstruktora klasy T hread należy przekazać obiekt klasy implementującej ten interfejs, a następnie dla utworzonego wątku należy wywołać metodę start().
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.TimeUnit;
4
5
public class TestRunnable implements Runnable {
6
private int t;
7
8
public TestRunnable(int t) {
9
this.t = t;
10
}
11
12
public void run() {
13
for (int i = 0; i < 20; i++) {
14
System.out.println(this);
15
try {
16
TimeUnit.MILLISECONDS.sleep(t);
17
// lub Thread.sleep(t);
18
} catch (InterruptedException e) {
19
e.printStackTrace();
20
}
21
}
22
}
23
24
public static void main(String[] args) {
25
Thread t1 = new Thread(new TestRunnable(1000));
26
t1.start();
27
Thread t2 = new Thread(new TestRunnable(10000));
28
t2.start();
29
}
30
}
Przykład 1: src/pl/kielce/tu/lab13/TestRunnable.java {link}
Klasa T hread
W celu stworzenia nowego wątku w klasie dziedziczącej po T hread należy przesłonić metodę run(), a następnie dla wątku wywołać metodę start().
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.TimeUnit;
4
5
public class TestThread extends Thread {
6
private int t;
7
1Thinking in Java, Bruce Eckel, Wydanie IV, Helion, 2006
1
8
public TestThread(int t) {
9
this.t = t;
10
}
11
12
@Override
13
public void run() {
14
for (int i = 0; i < 20; i++) {
15
System.out.println(this);
16
try {
17
TimeUnit.MILLISECONDS.sleep(t);
18
} catch (InterruptedException e) {
19
e.printStackTrace();
20
}
21
}
22
}
23
24
public static void main(String[] args) {
25
TestThread t1 = new TestThread(1000);
26
t1.start();
27
TestThread t2 = new TestThread(10000);
28
t2.start();
29
}
30
}
Przykład 2: src/pl/kielce/tu/lab13/TestThread.java {link}
Wybrane metody klasy Thread:
- public void run() – metoda, która ma się wykonywać współbieżnie,
- public void start() – rozpoczyna wykonywanie nowego wątku,
- public void join() throws InterruptedException – oczekuje na zakończenie się wątku,
- public void interrupt() – przerywa działanie wątku,
- public final boolean isAlive() – sprawdza czy wątek się zakończył,
- public final boolean isDaemon() – sprawdza czy wątek jest wątkiem działającym w tle.
Proszę porównać działanie poniższego programu:
- z komentarzami,
- z jednym (dowolnym) komentarzem,
- bez komentarzy?
1
package pl.kielce.tu.lab13;
2
3
public class TestThreadDaemon extends Thread {
4
5
public static void main(String[] args) {
6
TestThread t1 = new TestThread(1000);
7
// t1.setDaemon(true);
8
t1.start();
9
TestThread t2 = new TestThread(10000);
10
// t2.setDaemon(true);
11
t2.start();
12
}
13
}
Przykład 3: src/pl/kielce/tu/lab13/TestThreadDaemon.java {link}
2
- public final boolean isInterrupted() – sprawdza czy działanie wątku zostało przerwane,
- public static void sleep(long millis) throws InterruptedException – usypia aktualny wątek na podany czas,
- public final void yield() – wstrzymuje wykonanie aktualnego wątku umożliwiając wykonywanie się innych wątków,
- public final void setPriority(int newPriority) – ustawia priorytet wątku,
- public final int getPriority() – pobiera priorytet wątku.
„Unikaj modyfikacji priorytetów wątków2, gdyż zwiększają zależność od platformy i stwarzają problemy z żywotnością.”
W Javie 1.5 dodano pakiet java.util.concurrent. Od Javy 1.5 zalecaną metodą uruchamiania wątków jest metoda wykorzystująca klasy z tego pakietu (Executors).
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.ExecutorService;
4
import java.util.concurrent.Executors;
5
import java.util.concurrent.TimeUnit;
6
7
public class TestCachedThreadPool implements Runnable {
8
9
private int t;
10
11
public TestCachedThreadPool(int t) {
12
this.t = t;
13
}
14
15
public void run() {
16
for (int i = 0; i < 20; i++) {
17
System.out.println(this);
18
try {
19
TimeUnit.MILLISECONDS.sleep(t);
20
} catch (InterruptedException e) {
21
e.printStackTrace();
22
}
23
}
24
}
25
26
public static void main(String[] args) {
27
Thread t1 = new Thread(new TestCachedThreadPool(1000)); 28
Thread t2 = new Thread(new TestCachedThreadPool(10000)); 29
ExecutorService e = Executors.newCachedThreadPool();
30
e.execute(t1);
31
e.execute(t2);
32
e.shutdown();
33
}
34
}
Przykład 4: src/pl/kielce/tu/lab13/TestCachedThreadPool.java {link}
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.ExecutorService;
4
import java.util.concurrent.Executors;
5
import java.util.concurrent.TimeUnit;
6
2Goetz B., Peierls T., Bloch J., Bowbeer J., Holmes D., Lea D., Java Współbieżność dla praktyków, Helion 2007
3
7
public class TestFixedThreadPool implements Runnable {
8
9
private int t;
10
11
public TestFixedThreadPool(int t) {
12
this.t = t;
13
}
14
15
public void run() {
16
for (int i = 0; i < 20; i++) {
17
System.out.println(this);
18
try {
19
TimeUnit.MILLISECONDS.sleep(t);
20
} catch (InterruptedException e) {
21
e.printStackTrace();
22
}
23
}
24
}
25
26
public static void main(String[] args) {
27
Thread t1 = new Thread(new TestFixedThreadPool(1000));
28
Thread t2 = new Thread(new TestFixedThreadPool(10000)); 29
ExecutorService e = Executors.newCachedThreadPool();
30
e.execute(t1);
31
e.execute(t2);
32
e.shutdown();
33
}
34
}
Przykład 5: src/pl/kielce/tu/lab13/TestFixedThreadPool.java {link}
2
Uruchamianie programów
1
package pl.kielce.tu.lab13;
2
3
import java.io.BufferedReader;
4
import java.io.IOException;
5
import java.io.InputStreamReader;
6
7
public class TestProcess {
8
public static void main(String[] args) throws IOException, 9
InterruptedException {
10
Runtime runtime = Runtime.getRuntime();
11
Process process = runtime.exec("ipconfig /all"); 12
BufferedReader input = new BufferedReader(new InputStreamReader(
13
process.getInputStream()));
14
String s = null;
15
while ((s = input.readLine()) != null) {
16
if (s.length() > 0) {
17
s = s.replace(’.’, ’_’);
18
s = s.replace(’:’, ’_’);
19
s = s.replace(’ ’, ’_’);
20
System.out.println(s);
21
}
22
}
23
input.close();
24
process.waitFor();
25
Process process2 = runtime.exec("notepad");
26
process2.waitFor();
4
27
System.out.println("end");
28
}
29
}
Przykład 6: src/pl/kielce/tu/lab13/TestProcess.java {link}
Od wersji Javy 1.5 metoda ProcessBulider.start() jest zalecaną metodą tworzenia procesów.
1
package pl.kielce.tu.lab13;
2
3
import java.io.BufferedReader;
4
import java.io.File;
5
import java.io.IOException;
6
import java.io.InputStreamReader;
7
8
public class TestProcessBuilder {
9
public static void main(String[] args) throws IOException, 10
InterruptedException {
11
12
ProcessBuilder processBuilder = new ProcessBuilder(
13
"C:\\WINDOWS\\system32\\ipconfig", "/all"); 14
processBuilder.directory(new File("C:\\"));
15
Process process = processBuilder.start();
16
17
BufferedReader input = new BufferedReader(new InputStreamReader(
18
process.getInputStream()));
19
String s = null;
20
while ((s = input.readLine()) != null) {
21
if (s.length() > 0) {
22
s = s.replace(’.’, ’_’);
23
s = s.replace(’:’, ’_’);
24
s = s.replace(’ ’, ’_’);
25
System.out.println(s);
26
}
27
}
28
input.close();
29
process.waitFor();
30
31
ProcessBuilder processBuilder2 = new ProcessBuilder(
32
"C:\\WINDOWS\\notepad");
33
Process process2 = processBuilder2.start();
34
process2.waitFor();
35
System.out.println("end");
36
}
37
}
Przykład 7: src/pl/kielce/tu/lab13/TestProcessBuilder.java {link}
3
Współdzielenie zasobów
Uwaga!!! Aplikacja może działać w nieskończoność. Czy aplikacja kiedykolwiek się zakończy? Czy jest moż-
liwe, aby value.i kiedykolwiek przyjęło wartości -1 lub 2 (działanie programu może być zależne od parametrów podanych na początku instrukcji)?
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.TimeUnit;
4
5
public class TestNoSynchronization extends Thread {
6
final private static int SIZE = 2;
5
7
static MyObject value = new MyObject();
8
9
@Override
10
public void run() {
11
while (true) {
12
switch (value.i) {
13
case 0:
14
value.i++; // 0 + 1 = 1 ???
15
break;
16
case 1:
17
value.i--; // 1 - 1 = 0 ???
18
break;
19
default:
20
System.err.println("ERROR " + value.i);
21
System.exit(1);
22
}
23
try {
24
TimeUnit.MICROSECONDS.sleep(10);
25
} catch (InterruptedException e) {
26
e.printStackTrace();
27
}
28
}
29
}
30
31
public static void main(String[] args) {
32
TestNoSynchronization[] t = new TestNoSynchronization[SIZE]; 33
for (int i = 0; i < SIZE; i++) {
34
t[i] = new TestNoSynchronization();
35
t[i].start();
36
}
37
}
38
39
static class MyObject {
40
int i = 0;
41
}
42
}
Przykład 8: src/pl/kielce/tu/lab13/TestNoSynchronization.java {link}
4
Synchronizowana metoda
Metoda synchronizowana może być wykonywana w tym samym czasie tylko przez jeden wątek np.
synchronized public int method(){
...
}
Uwaga!!! Aplikacja może działać w nieskończoność. Jaka jest różnica w działaniu programu po usunięciu komentarza (zaobserwowanie różnicy może być zależne od parametrów podanych na początku instrukcji laboratoryjnej) ?
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.TimeUnit;
4
5
public class TestSynchronizedMethod extends Thread {
6
final private static int SIZE = 2;
7
8
static MyObject value = new MyObject();
9
10
@Override
6
11
public void run() {
12
while (true) {
13
switch (value.method()) {
14
case 0:
15
case 1:
16
break;
17
default:
18
System.err.println("ERROR ");
19
System.exit(1);
20
}
21
try {
22
TimeUnit.MICROSECONDS.sleep(10);
23
} catch (InterruptedException e) {
24
e.printStackTrace();
25
}
26
}
27
}
28
29
public static void main(String[] args) {
30
31
TestSynchronizedMethod[] t = new TestSynchronizedMethod[SIZE]; 32
for (int i = 0; i < SIZE; i++) {
33
t[i] = new TestSynchronizedMethod();
34
t[i].start();
35
}
36
}
37
38
static class MyObject {
39
40
private int i = 0;
41
42
/* synchronized */public int method() {
43
if (i == 0)
44
return ++i;
45
if (i == 1)
46
return --i;
47
return i;
48
}
49
}
50
}
Przykład 9: src/pl/kielce/tu/lab13/TestSynchronizedMethod.java {link}
5
Blok synchronizowany
W celu wykonania bloku synchronizowanego wymagane jest uzyskanie blokady obiekt synchronizującego.
W danym momencie tylko jeden wątek może uzyskać taką blokadę.
synchronized (obiektSynchronizujący) {
...
}
Uwaga!!! Aplikacja może działać w nieskończoność. Jaka jest różnica w działaniu programu po usunięciu komentarza (zaobserwowanie różnicy może być zależne od parametrów podanych na początku instrukcji laboratoryjnej) ?
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.TimeUnit;
4
5
public class TestSynchronizedBlock extends Thread {
7
6
final private static int SIZE = 2;
7
8
static MyObject value = new MyObject();
9
10
@Override
11
public void run() {
12
while (true) {
13
// synchronized (value)
14
{
15
switch (value.i) {
16
case 0:
17
value.i++;
18
break;
19
case 1:
20
value.i--;
21
break;
22
default:
23
System.err.println("ERROR " + value.i);
24
System.exit(1);
25
}
26
try {
27
TimeUnit.MICROSECONDS.sleep(10);
28
} catch (InterruptedException e) {
29
e.printStackTrace();
30
}
31
}
32
}
33
}
34
35
public static void main(String[] args) {
36
TestSynchronizedBlock[] t = new TestSynchronizedBlock[SIZE]; 37
for (int i = 0; i < SIZE; i++) {
38
t[i] = new TestSynchronizedBlock();
39
t[i].start();
40
}
41
}
42
43
static class MyObject {
44
int i = 0;
45
}
46
}
Przykład 10: src/pl/kielce/tu/lab13/TestSynchronizedBlock.java {link}
6
Monitor
Klasa Object zawiera monitor pozwalający synchronizować dostęp. Wybrane metody klasy Object:
- public final void wait() throws InterruptedException – wątek zostaje zatrzymany, aż do wywołania metody notif y() lub notif yAll(), istnieją także przesłonięte wersje tej metody ograniczające maksymalny czas czekania,
- public final void notify(), public final void notifyAll() – budzi wątek (lub wątki) czekające na monitorze obiektu.
Metody te mogą być tylko wywoływane w synchronizowanej metodzie lub synchronizowanym bloku!
1
package pl.kielce.tu.lab13;
2
3
import java.util.Random;
4
import java.util.concurrent.TimeUnit;
8
5
6
class Bufor {
7
int value;
8
}
9
10
class Reader extends Thread {
11
Bufor b;
12
13
Reader(Bufor b) {
14
this.b = b;
15
}
16
17
@Override
18
public void run() {
19
try {
20
for (int i = 0; i < 10; i++) {
21
synchronized (b) {
22
// Poczekaj na powiadomienie od Piszącego
23
System.err.println(">");
24
b.wait();
25
System.err.println(">> Czytający " + b.value); 26
// Powiadom czekającego Piszącego
27
b.notify();
28
System.err.println(">>>");
29
}
30
}
31
} catch (InterruptedException e) {
32
e.printStackTrace();
33
System.exit(1);
34
}
35
}
36
}
37
38
class Writer extends Thread {
39
40
Bufor b;
41
42
Writer(Bufor b) {
43
this.b = b;
44
}
45
46
Random r = new Random();
47
48
@Override
49
public void run() {
50
try {
51
for (int i = 0; i < 10; i++) {
52
synchronized (b) {
53
int t = r.nextInt(1000);
54
b.value = t;
55
TimeUnit.MICROSECONDS.sleep(t);
56
System.err.println("< Piszący " + b.value); 57
// Powiadom czekającego Czytającego
58
b.notify();
59
System.err.println("<<");
60
// Poczekaj na powiadomienie od Czytającego
61
b.wait();
62
System.err.println("<<<");
63
}
64
}
65
} catch (InterruptedException e) {
66
e.printStackTrace();
9
67
System.exit(1);
68
}
69
70
}
71
}
72
73
public class TestMonitor extends Thread {
74
75
public static void main(String[] args) {
76
Bufor b = new Bufor();
77
Writer p = new Writer(b);
78
Reader c = new Reader(b);
79
c.start();
80
p.start();
81
}
82
}
Przykład 11: src/pl/kielce/tu/lab13/TestMonitor.java {link}
7
Kolekcje synchronizowane
public static <T> List<T> synchronizedList(List<T> list) public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) public static <T> Set<T> synchronizedSet(Set<T> s) public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m) 8
Timer
Uruchamianie zadań o określonej porze i / lub z określonym odstępem czasu.
1
package pl.kielce.tu.lab13;
2
3
import java.util.Date;
4
import java.util.Timer;
5
import java.util.TimerTask;
6
7
public class TestTimer extends Thread {
8
9
public static void main(String[] args) {
10
MyTask task = new MyTask();
11
Timer timer = new Timer();
12
timer.schedule(task, new Date(), 1000);
13
}
14
15
static class MyTask extends TimerTask {
16
@Override
17
public void run() {
18
System.err.print("#");
19
}
20
}
21
}
Przykład 12: src/pl/kielce/tu/lab13/TestTimer.java {link}
„Klasa ScheduledThreadPoolExecutor 2 poprawnie radzi ze źle zachowującymi się zadaniami, więc nie ma powodów, by stosować klasę Timer w aplikacjach Javy 5.0 lub nowszych.”
1
package pl.kielce.tu.lab13;
2
3
import java.util.concurrent.ScheduledThreadPoolExecutor; 10
4
import java.util.concurrent.TimeUnit;
5
6
public class TestScheduledThreadPoolExecutor extends Thread {
7
public static void main(String[] args) throws InterruptedException {
8
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1); 9
executor.schedule(new MyTask(), 1, TimeUnit.SECONDS);
10
executor.shutdown();
11
executor.awaitTermination(1, TimeUnit.DAYS);
12
}
13
14
static class MyTask implements Runnable {
15
@Override
16
public void run() {
17
System.err.print("#");
18
}
19
}
20
}
Przykład 13: src/pl/kielce/tu/lab13/TestScheduledThreadPoolExecutor.java {link}
9
Java 1.5 a współbieżność
Dodatkowe elementy w Java 1.5 związane ze współbieżnością można znaleźć w pakietach:
- java.util.concurrent,
- java.util.concurrent.atomic,
- java.util.concurrent.locks.
Należą do nich między innymi:
- klasy atomowe np. AtomicLong, AtomicInteger, itp.,
- kolekcje bezpieczne wątkowo: ConcurrentHashM ap < K, V >, ConcurrentSkipListM ap < K, V >, ConcurrentSkipListSet < E >, ConcurrentLinkedQueue < E >,
- kolekcje kopiujące przy zapisie: CopyOnW riteArrayList, CopyOnW riteArraySet,
- kolejki blokujące: BlockingQueue < E >, BlockingDeque < E >,
- synchronizatory: CountDownLatch, CyclicBarrier, Exchanger, Semaphore, SynchronousQueue.
10
Przykładowa treść laboratorium
1. Proszę stworzyć aplikację graficzną zawierającą dwa komponenty graficzne. Zawartość obydwu komponentów powinna zmieniać się niezależnie co określony przedział czasu. Zadanie należy wykonać przy użyciu T hread, Runnable oraz T imer (lub ScheduledT hreadP oolExecutor).
2. Proszę stworzyć aplikację, w której zostanie wykorzystany współbieżny dostęp do tego samego obiektu przez kilka wątków przy użyciu synchronizowanych metod, synchronizowanych bloków oraz monitora klasy Object. Wątki powinny zostać uruchomione poprzez Executors.newCachedT hreadP ool() lub Executors.newCachedT hreadP ool().
3. Proszę stworzyć aplikację, która uruchomi dowolny, wykonywalny program znajdujący się na dysku (np. P aint.exe dla systemu operacyjnego W idnows).
11