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 Runnable
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
1
Thinking 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ów
, 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
2
Goetz 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 me-
tody 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 kompo-
nentó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