UNIVERZITET MEDITERAN FA K U LT E T Z A I N F O R M A C I O N.

Download Report

Transcript UNIVERZITET MEDITERAN FA K U LT E T Z A I N F O R M A C I O N.

UNIVERZITET MEDITERAN
FA K U LT E T Z A I N F O R M A C I O N E T E H N O L O G I J E
Programske NITI
JAVA PROGRAMIRANjE 2
DŽENAN STRUJIĆ
[email protected]
PODGORICA, 10.11.2008.GOD.
Uvod
 Nit je tok naredbi unutar programa, tj. Dio programa u
izvodenju.
 Niti se, za razliku od procesa, izvode unutar konteksta istog
programa i koriste resurse koje je alocirao program
(varijable, u-i tokove podataka i sl.).
 Program može sadržavati više niti koje se izvode
(kvazi)paralelno. Postotak procesorskog vremena koji ce
dobiti odredena nit zavisi o prioritetu, koji može primiti
vrijednost izmedu MIN_PRIORITY (1) i
MAX_PRIORITY(10), a inicijalno je postavljen na
NORM_PRIORITY (5). Nacin izvodenja niti zavisi o
operativnom sistemu, tj. nacinu dodjele procesora.
 JVM podržava mehanizam dodjele procesora prema fiksnom
prioritetu (preemptive round-robin scheduler). Podrška za
razdjeljivanje vremena (time slicing) nije ugradena, nego zavisi o
operativnom sistemu.
 Podrška za višenitni rad ugradena je u definiciju
programskog jezika Java. Višenitni rad u ostalim programskim
jezicima (npr. C) moguce je realizirati preko sistemskih
poziva operatibnog sistema(npr. fork – exec u UNIX-u).
 Korištenje niti pruža sljedece prednosti:
 Pridonosi interaktivnosti aplikacije. Ako se dugotrajna
izracunavanja i obrade obavljaju u posebnoj niti, aplikacija je I
dalje u mogucnosti komunicirati s korisnikom.
 Omogucuje korisniku paralelno obavljanje više zadataka.
Npr. Kod aplikacije za obradu dokumenata korisnik može
pokrenuti stampanje dijela dokumenta i nastaviti unos teksta.
 Ubrzava rad na sistemima s više procesora. Izvodenje
pojedinih niti raspodjeljuje se po raspoloživim procesorima,
cime se ubrzava izvodenje algoritma.
Stvaranje niti
 Pokretanjem Java programa JVM stvara jednu glavnu nit.
 Dodatne niti moguce je stvoriti na dva nacina:
1. Nasljedivanjem klase Thread i zamjenom metode run,
2. Implementacijom interfejsa Runnable, koje zahtijeva od klase
koja implementira interfejs definisanje metode run.
Ukoliko klasa kojom se defenise nit mora naslijediti neku drugu
klasu, tada treba koristiti interfejs Runnable. To je slucaj kod
definisanja appleta, koji moraju naslijediti klasu Applet
Primjer – nasljedivanje klase Thread
public class PrimjerNiti {
public static void main(String[] args) throws Exception {
new Nit("Prva").start();
new Nit("Druga").start();
new Nit("Treca").start();
}
}
class Nit extends Thread{
Nit(String ime) {
super(ime);
}
public void run() {
for (int i = 1; i <= 5; i++) {
try {
sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {}
System.out.println(this.getName() + ":\t" + i);
}
}
}
Primjer – implementacija interfejsa
Runnable
public class PrimjerNiti1 {
public static void main(String[] args) throws Exception {
new Nit("Prva");
new Nit("Druga");
new Nit("Treca");
}
}
class Nit implements Runnable{
Thread ovaNit;
Nit(String ime) {
ovaNit = new Thread(this, ime);
ovaNit.start();
}
public void run() {
for (int i = 1; i <= 5; i++) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {}
System.out.println(ovaNit.getName() + ":\t" + i);
}
}
}
Rezultati izvodenja u oba slucaja su
slicni:
Druga: 1
Treca: 1
Prva: 1
Druga: 2
Prva: 2
Druga: 3
Treca: 2
Prva: 3
Druga: 4
Treca: 3
Prva: 4
Druga: 5
Treca: 4
Treca: 5
Prva: 5
Metode klase Thread
 Metode su detaljno opisane u Java API dokumentaciji ...
 http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Threa
d.html
Životni ciklus niti
-Nit je u stanju NotRunnable u sljedecim slucajevima:
- pozvala je metodu sleep – mora proteci zadani broj milisekundi
- pozvala je metodu wait – objekt koji treba ispuniti uslov na koji nit ceka treba metodom
notify ili notifyAll dojaviti ispunjenje uslova
- ceka završetak u-i operacije – u-i operacija mora završiti
Sinhronizacija niti
 Niti se najcešce ne izvršavaju nezavisno, nego rade zajednicki
na obavljanju odredenog zadatka. To u pravilu zahtijeva
pristup dijeljenim podacima ili ulaznoizlaznim tokovima
podataka.
 Potrebno je osigurati kontrolu nad pristupom dijeljenim
podacima ili u-i tokovima, kako se ne bi desilo da dvije ili više
niti istovremeno pristupe odredenim podacima. Npr. u
slucaju baze podataka ili datoteke na disku to može narušiti
integritet podataka.
 Java implementira efikasni mehanizam kontrole pristupa
odredenom objektu, metodi, ili dijelu koda: Monitor.
Sinhronizacija niti - prekid
 Metoda interrupt omogucuje prekidanje niti koja se nalazi u
stanju cekanja.
 Sljedeci program šalje prekid niti svake sekunde:
public class PrimjerPrekida {
public static void main(String[] args) throws Exception {
Nit novaNit = new Nit("Nit");
novaNit.start();
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
novaNit.interrupt();
}
// kontrola preko dijeljene varijable
novaNit.kraj = true;
}
}
class Nit extends Thread {
boolean kraj = false;
Nit(String ime) {
super(ime);
}
public void run() {
while (!kraj) {
try {
sleep(100);
// for (int i=0; i<200000000; i++);
} catch (InterruptedException e) {
System.out.println(this.getName() + ": stigao prekid !!!");
}
}
System.out.println(this.getName() + ": kraj.");
}
}
Nit: stigao prekid !!!
Nit: stigao prekid !!!
Nit: stigao prekid !!!
Nit: stigao prekid !!!
Nit: stigao prekid !!!
Nit: kraj.
Ukoliko nit nije u stanju cekanja, zahtjev za prekid ce biti prihvacen tek kada nit izvrši neku
od metoda wait, join ili sleep. Ukoliko je u meduvremenu poslano više zahtjeva za prekid,
bice prihvacen samo zadnji.
Sinhronizacija niti - Monitor
 Monitor je mehanizam sinhronizacije koji omogucuje pristup
odredenoj dijeljenoj strukturi (objektu) istovremeno samo
jednom procesu ili niti.
 Java implementira monitor, koji se postavlja korištenjem
kljucne rijeci synchronized
class SinhroniziranaKlasa {
public synchronized nekaMetoda(...) {
...
}
public nekaDrugaMetoda(...) {
...
synchronized (this) {
. . . // sinhronizovani dio koda
}
}
 Takva metoda ili dio koda naziva se kriticni odsjecak..
Primjer
public class PrimjerKriticnogOdsjecka {
public static void main(String[] args) throws Exception {
Semafor sem = new Semafor();
MojThread t1 = new MojThread("1");
MojThread t2 = new MojThread("2");
MojThread t3 = new MojThread("3");
MojThread t4 = new MojThread("4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MojThread extends Thread {
private int brProc;
private StringBuffer suffix = new StringBuffer();
public MojThread(String str) {
super(str);
brProc = Integer.parseInt(str);
for (int i = 1; i < brProc; i++) {
suffix.append("\t");
}
}
public void run() {
System.out.println(suffix.toString() + brProc
+ ". pred kriticnim odsjeckom");
kriticniOdsjecak(brProc, suffix);
}
synchronized static void kriticniOdsjecak(int brProc,
StringBuffer suffix) {
System.out.println(suffix.toString() + brProc
+ ". u kriticnom odsjecku");
try {
sleep(2000L);
} catch (InterruptedException e) {}
System.out.println(suffix.toString() + " |");
System.out.println(suffix.toString() + " |");
System.out.println(suffix.toString() + brProc
+ ". napušta kriticni odsjecak");
}
}
 Dio koda kojim je realizovan kriticni odsjecak:
class MojThread extends Thread {
...
public void run() {
...
kriticniOdsjecak(brProc, suffix);
}
synchronized static void kriticniOdsjecak( . . . ) {
...
}
}
Sinhronizacija niti: wait - notify
 Monitor osigurava jedinstveni i nedjeljivi pristup odredenom




objektu, ali ne omogucuje uskladivanje rada pojedinih niti. Cest
primjer u kojem se zahtjeva uskladivanje rada pojedinih
procesa/niti je "proizvodac– potrošac" (producer-consumer), gdje
jedna nit proizvodi podatke, a druga ih koristi.
Metoda wait postavlja nit u red cekanja odredenog objekta i
privremeno oslobada objekt do izlaska iz stanja cekanja. Nit izlazi
iz stanja cekanja i ulazi u red aktivnih niti (koje se natjecu za
procesorsko vrijeme) u sljedecim slucajevima:
neka druga nit pozove metodu notify ili notifyAll za taj objekt,
neka druga nit pošalje zahtjev za prekid, ili
istekne zadani vremenski interval.
 Metoda notify odabire jednu nit iz skupa niti koje cekaju
monitor odredenog objekta i stavlja je u red aktivnih niti.
 Metoda notifyAll stavlja sve niti koje cekaju monitor
odredenog objekta u red aktivnih niti.
Primjer - semafor
public class Semafor {
private int sem;
Semafor() {
sem = 1;
}
Semafor(int s) {
sem = s;
}
public synchronized void waitSem() {
while (sem <= 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
--sem;
}
public synchronized void sigSem() {
++sem;
notifyAll();
}
}
public class PrimjerSemafora {
public static void main(String[] args) throws Exception {
Semafor sem = new Semafor();
MojThread t1 = new MojThread("1", sem);
MojThread t2 = new MojThread("2", sem);
MojThread t3 = new MojThread("3", sem);
MojThread t4 = new MojThread("4", sem);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
Rezultat izvodenja programa je isti (slican) kao u prethodnom primjeru.
class MojThread extends Thread {
private Semafor sem;
private int brProc;
private StringBuffer suffix = new StringBuffer();
public MojThread(String str, Semafor sem) {
super(str);
this.sem = sem;
brProc = Integer.parseInt(str);
for (int i = 1; i < brProc; i++) {
suffix.append("\t");
}
}
public void run() {
System.out.println(suffix.toString() + brProc
+ ". pred kriticnim odsjeckom");
sem.waitSem();
System.out.println(suffix.toString() + brProc
+ ". u kriticnom odsjecku");
try {
sleep(2000L);
} catch (InterruptedException e) {}
System.out.println(suffix.toString() + " |");
System.out.println(suffix.toString() + " |");
System.out.println(suffix.toString() + brProc
+ ". napušta kriticni odsjecak");
sem.sigSem();
}
}
Primjer – proizvodac-potrošac
 Program stvara niti Producer i Consumer, te objekt
Prodavnica preko kojega proizvodac i potrošac razmjenjuju
podatke.
public class PrimjerProducerConsumer {
public static void main(String[] args) {
Prodavnica s = new Prodavnica();
Producer p = new Producer(s);
Consumer c = new Consumer(s);
p.start();
c.start();
}
}
 Proizvodac generise deset brojeva u slucajnim vremenskim
intervalima trajanja do jedne sekunde. Zadnji generisani broj
je nula, što oznacava kraj podataka.
class Producer extends Thread {
private Prodavnica prodavnica;
public Producer(Prodavnica s) {
this.prodavnica = s;
}
public void run() {
for (int i = 10; i >= 0; i--) {
prodavnica.put(i);
System.out.println("Proizvodac postavlja: " + i);
try {
sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) { }
}
}
}
 Potrošac prihvaca i ispisuje podatake. Nula oznacava kraj
podataka.
class Consumer extends Thread {
private Prodavnica prodavnica;
public Consumer(Prodavnica s) {
this.prodavnica = s;
}
public void run() {
int podatak = 0;
do {
podatak = prodavnica.get();
System.out.println("Potrošac je primio: "
+ podatak);
} while (podatak > 0);
System.out.println("KRAJ !!!");
class Prodavnica {
private int sadrzaj;
private boolean noviPodatak = false;
public synchronized int get() {
while (noviPodatak == false) {
try {
wait();
} catch (InterruptedException e) { }
}
noviPodatak = false;
notifyAll();
return sadrzaj;
}
public synchronized void put(int podatak) {
while (noviPodatak == true) {
try {
wait();
} catch (InterruptedException e) { }
}
sadrzaj = podatak;
noviPodatak = true;
notifyAll();
}
}
Proizvodac postavlja: 10
Potrošac je primio: 10
Proizvodac postavlja: 9
Potrošac je primio: 9
Proizvodac postavlja: 8
Potrošac je primio: 8
Proizvodac postavlja: 7
Potrošac je primio: 7
Proizvodac postavlja: 6
Potrošac je primio: 6
Proizvodac postavlja: 5
Potrošac je primio: 5
Proizvodac postavlja: 4
Potrošac je primio: 4
Proizvodac postavlja: 3
Potrošac je primio: 3
Proizvodac postavlja: 2
Potrošac je primio: 2
Proizvodac postavlja: 1
Potrošac je primio: 1
Proizvodac postavlja: 0
Potrošac je primio: 0
KRAJ !!!
Klase Timer i TimerTask
 Klase Timer i TimerTask omogucuju izvršavanje odredenog zadatka
nakon isteka zadanog vremenskog intervala ili u zadano vrijeme.
 potrebno je:
 definisati podklasu klase TimerTask cija metoda run() sadrži kod koji
obavlja traženi zadatak,
 stvoriti nit stvaranjem instance klase Timer,
 stvoriti instancu definisane potklase klase TimerTask,
 rasporediti nit za izvodenje.
 U sljedecem primjeru zadatak se rasporeduje za periodicko
izvodenje svake dvije sekunde. Nakon pet izvodenja zadatak se više
ne rasporeduje.
import java.util.Timer;
import java.util.TimerTask;
public class PrimjerTimera {
Timer timer;
public static int brojPoziva = 1;
public PrimjerTimera(int sekunde) {
timer = new Timer();
timer.schedule(new NitTimera(), sekunde*1000,
sekunde*1000);
}
class NitTimera extends TimerTask {
public void run() {
System.out.println("\tZadano vrijeme isteklo "
+ brojPoziva + ". puta");
if (brojPoziva++ == 5) {
timer.cancel();
}
}
}
public static void main(String args[]) {
System.out.println("Rasporedivanje niti timera:");
new PrimjerTimera(2);
System.out.println("Nit rasporedena ...");
}
}
Rasporedivanje niti timera:
Nit rasporedena ...
Zadano vrijeme isteklo 1. puta
Zadano vrijeme isteklo 2. puta
Zadano vrijeme isteklo 3. puta
Zadano vrijeme isteklo 4. puta
Zadano vrijeme isteklo 5. puta
 Metoda schedule održava tacno vrijeme izmedu dva
uzastopna izvršenja niti. Ako se traži vremenska tacnost u
duljem periodu, treba koristiti metodu scheduleAtFixedRate.