Vl kna a konkurentn v po ty
This presentation is the property of its rightful owner.
Sponsored Links
1 / 50

Vl ákna a konkurentné výpočty PowerPoint PPT Presentation


  • 102 Views
  • Uploaded on
  • Presentation posted in: General

Vl ákna a konkurentné výpočty. dnes bude: konkurentné vs. p aral elné výpočty , vl ákna (threads) vs. proces y, komunikácia cez rúry (pipes), s ynchronizácia a k ritick á sekcia (semaf óry ), deadlock literat úra : Thinking in Java, 3rd Edition , 13.kapitola,

Download Presentation

Vl ákna a konkurentné výpočty

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Vl kna a konkurentn v po ty

Vlákna a konkurentné výpočty

dnes bude:

  • konkurentné vs. paralelné výpočty,

  • vlákna (threads) vs. procesy,

  • komunikácia cez rúry (pipes),

  • synchronizácia a kritická sekcia (semafóry),

  • deadlock

    literatúra:

  • Thinking in Java, 3rd Edition, 13.kapitola,

  • Concurrency Lesson, resp. Lekcia Súbežnosť,

  • Java Threads Tutorial,

  • Introduction to Java threads

    Prerekvizície (nepredpokladám, že ste zažili):

  • prednáška 29: vlákna v Pascale, http://pascal.input.sk/index.php/Start

    Cvičenia:

  • simuláciekonzolové či applety (ak treba, použiť existujúci kód),

  • napr. iné triedenie, iné guličky, plavecký bazén, lienky na priamke, ...


Procesy a vl kna

Procesy a vlákna

  • každý program v Jave obsahuje aspoň jedno vlákno (main),

  • okrem užívateľom definovaných vlákien, runtime spúšťa tiež “neviditeľné” vlákna, napríklad pri čistení pamäte,

  • pri aplikáciach, ktoré budú obsahovať GUI sa nezaobídeme bez vlákien,

  • každý bežný operačný systém podporuje vlákna aj procesy,

  • v prípade jedno/dvoj-procesorového systému OS musí zabezpečiť [preemptívne] prerozdelenie času medzi vlákna a procesy,

  • nepreemptívne plánovanie vymrelo s Win 3.0 a Win98/16bit,

  • na preemptívnom princípe ‘každý chvíľku ťahá pílku’ vzniká konkukrentný výpočet miesto skutočne paralelného výpočtu,

  • vlákna môžeme chápať ako jednoduchšie procesy, (ako jednoduchšie, to uvidíme)…

  • správu procesov riadi plánovač OS


Proces

Proces

  • je nezávislá entita na úrovni aplikácie, má svoj kód, dáta, systémový zásobník, program counter, ...

  • procesy môžu komunikovať cez rúry (pipes), súbory, či sokety (zástrčky ,

  • runtime java vytvorí normálne jeden proces (ale bežne viac vlákien),

  • v jave vieme vytvoriť nový proces (trieda ProcessBuilder), ale urobíme si len malú ukážku:

prvy: prvyprvyprvyprvyprvyprvy

druhy: druhydruhydruhydruhyd

import java.lang.*;import java.io.*;publicclass VytvorProces {publicstaticvoid main(String[] args) throws IOException {final ProcessBuilder pb =// vytvor nový proces/naklonuj sám sebanewProcessBuilder("java","VytvorProces","druhy");// cmdline+argsif (args[0].equals("prvy"))// ak si prvý process, tak spusti druhýpb.start(); // ak si druhý process, nepúšťaj ničFileWriter fw = new FileWriter(args[0]);// otvor súbor prvy/druhywhile (true)fw.write(args[0]);// bezhlavo píš do súboru}}

Súbor: VytvorProces.java


Vl kna

Vlákna

  • odteraz sa venujeme už len vláknam,

  • vlákna sú „zjednodušené“ procesy,

  • každý proces má aspoň jedno (hlavné ) vlákno,

  • vlákna v rámci procesu zdieľajú prostriedky ako

    • pamäť (dáta, premenné, ...),

    • súbory a streamy,

  • zdieľanie prostriedkov niekedy pomáha (ako forma medzivláknovej komunikácie) a inokedy obmedzuje,

  • preto na riadenie zdieľania prostriedkov java poskytuje zabudované mechanizmy akými sú synchronizácia, či semafóry,

  • správu vlákien si riadi java runtime.

    Pekná inšpirácia a príklady

    http://www.cs.ubc.ca/~harrison/Java/sorting-demo.html

    http://maven.smith.edu/~thiebaut/java/sort/demo.html

    http://www.doc.ic.ac.uk/~jnm/book/book_applets/concurrency.html


O n s ak o vl knach

Čo nás čaká o vláknach

  • vlákno je objekt nejakej podtriedy triedy Thread (package java.lang.Thread),

  • vlákno vieme vytvoriť (new Thread() , new SubTread()),

  • vlákno vieme spustiť (metóda Thread.start()),

  • vláknu vieme povedať, čo má robiť (vmetóde run() {…}),

  • vlákno vieme pozastaviť (Thread.yield()) a dať šancu iným vláknam,

  • vláknam vieme rozdať priority (Thread.setPriority()), akými bojujú o čas,

  • vlákno vieme uspať na dobu určitú (Thread.sleep()),

  • na vlákno vieme počkať, kým dobehne (Thread.join()),

  • na vlákno vieme prerušiť (Thread.interrupt()).

    Praktický pohľad na vlákna:

  • programy s vláknami sa ťažšie ľadia,

  • pri dvoch behoch rovnakého programu nemáme zaručené, že dôjde k rovnakej interakcii vlákien, ak však interagujú,

  • ľadenie chybnej synchronizácie vlákien je podobne náročné ako ak vám, “ktosi” prepisuje pamäť,

  • vo všeobecnosti, na konkurentné výpočty nie sme veľmi trénovaní...


Vl kna na pr kladoch

Vlákna na príkladoch

Krok za krokom:

  • nasledujúci príklad vytvorí a spustí 5 vlákien,

  • všetky vlákna sú podtriedou Thread,

  • konštruktor SimpleThread volá konštruktor triedy Thread s menom vlákna,

  • metóda getName() vráti meno vlákna,

  • každé vlákno si v cykle počíta v premennej countDown od 5 po 0 (metóda run()),

  • pri prechode cyklu vypíše svoj stav (metóda toString()),

  • keď countDown = 0 metóda run() dobehne, život vlákna končí,

  • aj keď si to priamo neuvedomujeme, vlákna zdieľajú výstupný stream System.out tým, že do neho „súčasne“ píšu.


Vytvorenie vl kna

Vytvorenie vlákna

#1: 5

#1: 4

#1: 3

#1: 2

#1: 1

#3: 5

#3: 4

#3: 3

#3: 2

#3: 1

#5: 5

#5: 4

#5: 3

#5: 2

#5: 1

#2: 5

#2: 4

#2: 3

#2: 2

#2: 1

#4: 5

#4: 4

#4: 3

#4: 2

#4: 1

public class SimpleThread extends Thread {

private int countDown = 5;

private static int threadCount = 0;

public SimpleThread() {

super("" + (++threadCount)); // meno vlákna je threadCount

start();// naštartovanie vlákna run()

}

public String toString() {// stav vlákna

return "#" + getName() + ": " + countDown;

}

public void run() {// toto vlákno robí, ak je spustené

while(true) {

System.out.println(this);// odpočítava od countDown

if(--countDown == 0) return;

}

}

public static void main(String[] args) {

for(int i = 0; i < 5; i++)

new SimpleThread();// vytvorenie vlákna, ešte nebeží

}

}

Súbor: SimpleThread.java


Za a enie vl kna

Zaťaženie vlákna

#1: 5

#2: 5

#3: 5

#4: 5

#5: 5

#2: 4

#1: 4

#4: 4

#3: 4

#5: 4

#2: 3

#4: 3

#1: 3

#3: 3

#5: 3

#2: 2

#4: 2

#1: 2

#5: 2

#3: 2

#2: 1

#4: 1

#1: 1

#5: 1

#3: 1

  • v predchádzajúcom príklade sme nemali pocit, že by vlákna bežali súbežne,

  • lebo čas pridelený plánovačom k ich behu im postačoval, aby vlákno prebehlo a skončilo metódu run(),

  • preto pridajme im viac roboty, príklad je umelý ale ilustratívny

    public void run() {

    while(true) {

    System.out.println(this);

    for(int j=0; j<50000000; j++) {// kým toto zráta

    double gg = 0-Math.PI+j+j-j+Math.PI;// zapotí sa ...

    }

    if(--countDown == 0) return;

    }

    }

  • toto je jedna možnosť, ako pozdržať/spomaliť výpočet vlákna, ktorá však vyčerpáva procesor (pozrite si CPU load),

  • ak chceme, aby sa počas náročného výpočtu vlákna dostali k slovu aj iné vlákna, použijeme metódu yield() – „daj šancu iným“, resp. nastavíme rôzne priority vlákien, viď nasledujúce príklady

Súbor: SimpleThread2.java


Pozastavenie u vo nenie vl kna

Pozastavenie/uvoľnenie vlákna

  • metóda yield() zistí, či nie sú iné vlákna v stave pripravenom na beh (Runnable),

  • ak sú, dá im prednosť.

    public void run() {

    while(true) {

    System.out.println(this);

    for(int j=0; j<50000000; j++) {// kým toto zráta

    // zapotí sa ...

    double gg = 0-Math.PI+j+j-j+Math.PI;

    }

    yield();// daj šancu iným

    if(--countDown == 0) return;

    }

    }

  • iná možnosť spočíva v nastavení priorít vlákien,

  • pripomeňme si, že vlákna nie sú procesy na úrovni OS,

  • plánovač vlákien pozná 10 úrovní priorít z intervalu MAX_PRIORITY(10), MIN_PRIORITY(1),ktoré nastavíme pomocousetPriority(int newPriority)

#1: 5

#2: 5

#4: 5

#3: 5

#5: 5

#2: 4

#4: 4

#3: 4

#5: 4

#1: 4

#2: 3

#1: 3

#4: 3

#3: 3

#5: 3

#2: 2

#4: 2

#3: 2

#1: 2

#5: 2

#2: 1

#4: 1

#3: 1

#5: 1

#1: 1

Súbor: YieldingThread.java


Priority vl kna

Priority vlákna

Thread[Thread-1,1,main]

Thread[Thread-0,10,main]

Thread[Thread-2,1,main]

Thread[Thread-0,10,main]

Thread[Thread-0,10,main]

Thread[Thread-1,1,main]

Thread[Thread-0,10,main]

Thread[Thread-1,1,main]

Thread[Thread-0,10,main]

Thread[Thread-1,1,main]

Thread[Thread-1,1,main]

Thread[Thread-4,1,main]

Thread[Thread-3,1,main]

Thread[Thread-4,1,main]

Thread[Thread-3,1,main]

Thread[Thread-4,1,main]

Thread[Thread-3,1,main]

Thread[Thread-4,1,main]

Thread[Thread-3,1,main]

Thread[Thread-4,1,main]

Thread[Thread-3,1,main]

Thread[Thread-5,1,main]

Thread[Thread-2,1,main]

Thread[Thread-5,1,main]

Thread[Thread-2,1,main]

Thread[Thread-5,1,main]

Thread[Thread-5,1,main]

Thread[Thread-2,1,main]

Thread[Thread-5,1,main]

Thread[Thread-2,1,main]

public class PriorityThread extends Thread {

private int countDown = 5;

private volatile double d = 0; // d je bez optimalizácie

public PriorityThread (int priority) {

setPriority(priority);// nastavenie priority

start();// spustenie behu vlákna

}

public void run() {

while(true) {

for(int i = 1; i < 100000; i++)

d = d + (Math.PI + Math.E) / (double)i;

System.out.println(this);

if(--countDown == 0) return;

}

}

public static void main(String[] args) {

new PriorityThread (Thread.MAX_PRIORITY); //#0=10

for(int i = 0; i < 5; i++)

new PriorityThread (Thread.MIN_PRIORITY); //#i=1

}

}

Súbor: PriorityThread.java


Interface run n able

Interface Runnable

  • iná možnosť, ako vytvoriť vlákno, je trieda, ktorá implementuje interface Runnable,

  • vtedy vytvoríme vlákno pomocou konštruktora new Thread(this),

  • takáto trieda potom musí obsahovať metódu void run(), ktorá sa spustí po start(), POZOR, NIE run() !!!!

    Okrem toho je nasledujúci príklad prvým príkladom appletu:

  • podtrieda triedy Applet poskytuje grafické rozhranie,

  • bližšie na budúcej prednáške,

  • ale škoda robiť simulácie procesov bez možnosti si ich graficky zobraziť...

    Čo k appletom potrebujeme na štart:

  • nasledujúci applet má:

    • inicializačnú metódu void init(),

    • po nej sa volá metóda void start(),

    • zobrazenie/prekreslenie appletu je metóda void paint(Graphics g),

    • tutorial pre grafiku napr. tu

  • spúšťa sa v Eclipse ako applet Alt+Shift+X-A, resp. pomocou appletviewer-a


Ru i ka

import java.applet.*;

import java.awt.*;

public class Rucicka extends Applet

implements Runnable {

int width, height;

int i = 0;

Thread t = null;

public void init() {

width = getSize().width;

height = getSize().height;

setBackground( Color.BLUE );

}

public void start() {

t =new Thread( this );

t.start();

}

}

Ručička

public void run() {

try {

while (true) {

i++; i %= 60;

repaint();

t.sleep( 1000 );

}

} catch (InterruptedException e) { /*…*/ }

}

public void paint( Graphics g ) {

g.setColor( Color.ORANGE );

g.drawLine( width/2, height/2, (int)(width/2+width/3*Math.cos(i/30.0*Math.PI)), (int)(height/2+height/3*Math.sin(i/30.0*Math.PI)));

}

Súbor: Rucicka.java


Guli ky v krabici

Guličky v krabici

  • nasledujúci príklad ilustruje simuláciu dvoch jednoduchých „procesov“,

  • v krabici lietajú dve rôznofarebné guličky,

  • každá z guličiek je simulovaná jedným vláknom,

  • toto vlákno si udržiava lokálny stav simulovaného objektu, t.j.

    • polohu, súradnice [x,y],

    • smer, vektor rýchlosti [dx, dy],

    • farbu, event. rýchlosť, ...

  • metóda run() počíta nasledujúci stav (polohu, smer) objektu (guličky),

  • treba k tomu trochu “fyziky” (lebo uhol dopadu sa rovná uhlu odrazu),

  • keďže strany krabice sú rovnobežne so súradnicami, stačí si uvedomiť, že

    • ak gulička nenarazí, jej nová poloha je [x+dx, y+dy],

    • ak gulička narazí, zmení sa jej smerový vektor na [Ŧdx, Ŧdy],

  • po každom kroku simulácie si vlákno vynúti prekreslenie (časti) appletu, t.j. vlákno má odkaz na hlavný program (applet),

  • hlavný program (applet) len:

    • vytvorí obe vlákna a naštartuje ich,

    • vykreslí polohu/stav guličiek (to musí vidieť ich súradnice, ktoré sú vo vláknach)


Guli ky thread

Guličky - thread

public class Gulicka extends Thread {

public int x = ..., y = …, dx = …, dy = …// stav guličky

Ball ap; // odkaz na hlavný applet, uložíme si v konštruktore

public void run() {

while (true) {

x += dx; y += dy;// urob krok

if (x >= sizeX) {// odrážanie od stien

x = sizeX - (x-sizeX); dx = -dx;

} else if (y >= sizeY) {

y = sizeY - (y-sizeY); dy = -dy;

} else if (x < 0) {

x = -x; dx = -dx;

} else if (y < 0) {

y = -y; dy = -dy;

}// simulácia má svoje rezervy v rohoch

ap.repaint();// prekresli applet s gulickami

try {

sleep(10); // pockaj 0.01 s – simuluj krok

} catch(Exception e) {…}

}

}

}

Súbor: Gulicka.java


Guli ky applet

Guličky - applet

public class Ball extends Applet {

Gulicka th1, th2;// dve vlákna pre gulicky

. . . . .

public void start() {

th1 = new Gulicka (this);// vytvor vlákno pre modrú

th1.start();// štartuj modrú guličku

th2 = new Gulicka (this); // vytvor vlákno červenej

th2.start();// štartuj červenú guličku

}

public void paint(Graphics g) {

// vypíš ich ubehnuté kroky do stavového riadku appletu

g.setColor(Color.BLUE);

g.drawRect(th1.x,th1.y,2,2);// pozri do vlákna

//na súradnice modrej

g.setColor(Color.RED);// pozri do vlákna

g.drawRect(th2.x,th2.y,2,2); // na súradnice červenej

}

. . . .

}

Súbor: Ball.java


Hra bomba t t

Hra Bomba-Štít

public class Bojovnici extends Applet {

final int N = 100;

Bojovnik[] bojovnik = new Bojovnik[N]; // pole všetkých bojovníkov

Color[] cols = {Color.RED, Color.BLUE, Color.GREEN, Color.CYAN, Color.YELLOW};

public void init() {// inicializácia appletu

resize(300,300);

Random rnd = new Random();// generátor náhody

for(int i=0; i<N; i++)// vytvorenie bojovníkov

bojovnik[i] = new Bojovnik(this, // čobojovník to vlákno

rnd.nextInt(300),rnd.nextInt(300),// náhodná pozícia na začiatok

cols[rnd.nextInt(cols.length)]);// farba bojovníka pre efekt

for(int i=0; i<N; i++) {// priradenie zabijáka a štítu

bojovnik[i].zabijak(bojovnik[(i+1)%N]); // nasledujúci je bomba-killer

bojovnik[i].stit(bojovnik[(i>0)?i-1:N-1]); // predchádzajúci je štít-defender

}

for(int i=0; i<N; i++)// spustenie všetkých vlákien

bojovnik[i].start();

}

Hraje N ľudí, každý má určený jedného hráča ako štít, jedného ako bombu, pričom sa snaží postaviť tak, aby ho štít chránil pred bombou (t.j. boli v priamke)

Súbor: Bojovnici.java


Vykreslenie bojovn kov

Vykreslenie bojovníkov

import java.applet.*;

import java.awt.*;

import java.util.*;

public class Bojovnici extends Applet {

. . . . . .

public void paint(Graphics g) {

for(int i=0; i<N; i++) {

g.setColor(bojovnik[i].col);

g.drawOval((int)Math.round(bojovnik[i].x),

(int)Math.round(bojovnik[i].y),

3,3);

}

}

}

Súbor: Bojovnici.java


Spr vanie bojovn ka

Správanie bojovníka

public class Bojovnik extends Thread {// lokálny stav bojovníka

public double x,y; //public int x,y;// --- súradnice

public Color col;// --- farba

Bojovnik killer, defender;// kto je jeho bomba a štít

Bojovnici applet;// pointer na nadradený applet

public Bojovnik(Bojovnici applet, int x, int y, Color col) {// konštruktor

this.x = x; this.y = y; this.col = col;this.applet = applet;}

public void zabijak(Bojovnik killer) {this.killer = killer;} // set killer

public void stit(Bojovnik defender) {this.defender=defender;}// set defender

public void run() {

while (true) {// súradnice bodu, kam sa treba teoreticky postaviť, aby

// defender bol v strede medzi mnou a killerom

double xx = 2*defender.x - killer.x; // rovnica priamky, nič viac...

double yy = 2*defender.y - killer.y;

double laziness = 0.1;// parameter lenivosti (0.0-1.0)

x = (xx-x)*laziness+x;// ako rýchlo smerujem do xx,yy

y = (yy-y)*laziness+y;// nové súradnice bojovníka

applet.repaint();

try { sleep(100); } catch(Exception e) {..} // 0.1 sek. Konštanta pre efekt

}

}

Súbor: Bojovnik.java


Trojuholn ky

Trojuholníky

  • nasledujúca hra trojuholníky je analógia hry Bomba-štít,

  • určení hráči majú tvoriť vrcholy rovnostranného trojuholníka,

  • simulácia je však urobená tak, že

    • hlavné vlákno (Trojuholniky) vykresluje stav (polohu a farby trojuholníkov)

      for(int i=0; i<th.N; i++) {

      g.setColor(cols[i%cols.length]);

      g.drawRect(th.x[i],th.y[i],3,3);

      g.drawLine(th.x[i-1],th.y[i-1],th.x[i+1],th.y[i+1]);

      g.drawLine(th.x[i],th.y[i],th.x[i+1],th.y[i+1]);

      g.drawLine(th.x[i-1],th.y[i-1],th.x[i],th.y[i]);

      }

    • jedno pomocné vlákno (vrcholy) simuluje pohyb všetkých bodov

    • ako zo súradníc A, B vyrátame súradnice vrchola C rovnostranného trojuholníka ? ... Trochu matiky...

    • ako sa tam dostaneme z D?

C

D

A

B


Hra trojuholn ky

Hra Trojuholníky

public class Vrcholy extends Thread {

Trojuholniky applet;

int N = 32;

int[] x; // x-ové súradnice bodov

int[] y;// y-ové súradnice bodov

public void run() {

int i = 0;

while (true) {

int i1 = i-1;if (i1 < 0) i1 = N-1;

int i2 = i+1;if (i2 == N) i2 = 0;

// vypočítaj nové súradnice bodu x[i],y[i] s vrcholmi x[i1],y[i1] a x[i2],y[i2]

double laziness = 0.01;

double new_X = x[i] + (double)(X-x[i])*laziness;

double new_Y = y[i] + (double)(Y-y[i])*laziness;

i++;if (i == N) i=0;// ďalší bod

applet.repaint();

try { sleep(4);} catch(Exception e) {}

}

Súbor: Trojuholnik.java,

Vrcholy.java


Preteky v trieden

Preteky v triedení

  • motivačný príklad o triedeni na začiatku prednášky je trochu podvod,

  • bežia tam síce súbežné vlákna, ale nebežia v jave, ale v browseri,

  • ďalší príklad je pretekom 4 triediacích algoritmov v jave,

  • hlavný applet je rozdelený na 4 panely (SortPanel extends java.awt.Panel),

  • každý SortPanel

    • náhodne vygeneruje pole na triedenie,

    • vytvorí vlákno triedy SortThread a spustí,

  • každý SortPanel si vyvorí miesto na kreslenie paličiek (SortCanvas extends java.awt.Canvas),

  • SortThread triedi vygenerované pole daným algoritmom (parameter ”buble”),

  • SortCanvas poskytuje SortThread metódu swap(i,j) – prvky i, j sa vymenili

  • SortCanvas zabezpečuje vykreslovanie paličiek do SortPanelu,

  • vymenené paličky (hi, lo) znázorní čierne,

  • Random sort je jediný (len mne) známy algo triedenia horši ako bublesort

i = random(); j = random();

if (i<j && a[i] > a[j]) { int pom = a[i]; a[i] = a[j]; a[j] = pom; }


Sorty

Sorty

public class SortApplet extends Applet {

SortPanel buble, quick, merge, random;

public void init() { ....

buble = new SortPanel("Buble",Color.MAGENTA); add(buble);

quick = new SortPanel("Quick",Color.BLUE); add(quick);

merge = new SortPanel("Merge",Color.RED); add(merge);

random = new SortPanel("Random",Color.GREEN); add(random);

}

public void start() {

buble.start(); quick.start(); merge.start(); random.start();

}

}

Súbor: SortApplet.java


Sortpanel canvas

SortPanel, Canvas

public class SortPanel extends Panel {

SortThread thread ;

SortCanvas can;

int[] a;

. . . .

public void start() {

a = new int[100];

for(int i=0; i < a.length; i++)

a[i] = (int)(200*Math.random());

thread =

new SortThread(can, algo, a);

thread.start();

}

}

public class SortCanvas

extends Canvas {

int lo, hi;// ktoré dve sa vymenili

public void swap(int i, int j) {

lo = i; hi = j;

}

public void update(Graphics g) {

g.clearRect(0,0,200,200);

Color cl = g.getColor();

for(int i=0; i < sp.a.length; i++) {

g.setColor((i == lo || i == hi)?

Color.BLACK:cl);

g.drawLine(2*i,sp.a[i],2*i,0);

//g.drawLine(2*i,sp.a[i],2*i,sp.a[i]);

}

}

}

Súbor: SortPanel.java

Súbor: SortCanvas.java


Randoms rt

RandomSrt

public class SortThread extends Thread {

SortCanvas can;

public void run() {

if (algo.equals("Buble")) bubleSort(a);

. . . .

else

randomSort(a);

}

void swap(int i, int j) {

can.swap(i,j);

can.repaint();

try{ sleep(10); } catch(Exception e) {}

}

void randomSort(int a[]) {

while(true) {

int i = (int)((a.length-1)*Math.random());

int j = (int)((a.length-1)*Math.random());

swap(i,j);

if (i<j && a[i] > a[j]) {

int pom = a[i]; a[i] = a[j]; a[j] = pom;

}

}

}

Súbor: SortThread.java


Korytna ky

Korytnačky

  • ďalšie dva príklady sú simulácii viacerých korytnačiek,

  • korytnačie príkazy poznáte z prednášky Delphi,

  • 1.verzia (Turtles), podobná triedeniam, rozdelíme plochu appletu štyrom korytnačkám,

  • vlákna nezávisle píšu do appletu, ale nezdieľajú žiaden spoločný Canvas,

  • 2.riešenie (Korytnačky) maľuje do toho istého priestoru – tu musíme byť opatrnejší...

    Nabudúce:

  • ak viacero vlákien chce zdieľať jeden zdroj/prostriedok (pamäť, stream, ...) musíme byť opatrnejší,

  • java ponúka prostriedky na synchronizáciu súbežných vlákien,

  • semafóry na riešenie kritických oblastí ... situácií...


Korytna ky1

Korytnačky

Korytnačí program:

for(int i=0; i < steps; i++) {

Dopredu(init);

init+=delta;

Vpravo(uhol);

}

public void Vlavo(float Uhol) {

dir -= Uhol;

}

public void Vpravo(float Uhol) {

dir += Uhol;

}

public void Dopredu(float Dlzka) {

double X1 = X + (float)(Dlzka*Math.cos(dir*2*Math.PI/360));

double Y1 = Y + (float)(Dlzka*Math.sin(dir*2*Math.PI/360));

sp.can.getGraphics().drawLine((int)X,(int)Y,(int)X1,(int)Y1);

X = X1; Y = Y1;

}

Súbor: Turtles.java, TurtlePanel.java, TurtleCanvas.java, TurtleThread.java


Korytna ky e te raz

Korytnačky ešte raz...

public class Korytnacky extends Applet {

Korytnacka k1, k2, k3, k4;

BufferedImage bufImage =

new BufferedImage(sizeX, sizeY,

BufferedImage.TYPE_INT_ARGB);

public void start() {

k1 = new Korytnacka(this, 100,100, 0, 1, 91);

k1.start();

// . . . .

k4 = new Korytnacka(this, 100, 300, 0, 1, 59); k4.start();

}

public void paint(Graphics g) {

g.drawImage(bufImage,0,0,Color.BLUE,this);// off-screen image

}

}

Súbor: Korytnacky.java


Vl kna a konkurentn v po ty pokra ovanie

Vlákna a konkurentné výpočty(pokračovanie)

dnes bude:

  • komunikácia cez rúry (pipes),

  • synchronizácia a kritická sekcia (semafóry),

  • deadlock

    literatúra:

  • Thinking in Java, 3rd Edition, 13.kapitola,

  • Concurrency Lesson, resp. Lekcia Súbežnosť,

  • Java Threads Tutorial,

  • Introduction to Java threads

    Prerekvizície (predpokladám, že ste zažili):

  • prednáška 29: vlákna v Delphi, http://delphi.input.sk/index.php/Start

    Cvičenia:

  • simuláciekonzolové či applety (ak treba, použiť existujúci kód),

  • napr. iné triedenie, iné guličky, plavecký bazén, lienky na priamke, ...


Pozastavenie uspanie vl kna

Pozastavenie/uspanie vlákna

  • zaťaženie vlákna (nezmyselným výpočtom) vyčerpáva procesor, potrebujeme jemnejšiu techniku,

  • nasledujúci príklad ukáže, ako uspíme vlákno bez toho aby sme zaťažovali procesor nepotrebným výpočtom,

  • vlákno uspíme na čas v milisekundáchmetódou Thread.sleep(long millis) throws InterruptedException,

  • spánok vlákna môže byť prerušený metódou Thread.interrupt(), preto pre sleep musíme ošetriť výnimku InterruptedException,

  • ak chceme počkať, kým výpočeť vlákna prirodzene dobehne (umrie), použijeme metódu Thread.join()

  • ak chceme testovať, či život vlákna bol prerušený, použijeme metódu boolean isInterrupted(), resp. Thread.interrupted().


Uspatie vl kna

Uspatie vlákna

#1: 5

#1: 4

#1: 3

#1: 2

#1: 1

--

#2: 5

#2: 4

#2: 3

#2: 2

#2: 1

--

#3: 5

#3: 4

#3: 3

#3: 2

#3: 1

--

#4: 5

#4: 4

#4: 3

#4: 2

#4: 1

--

#5: 5

#5: 4

#5: 3

#5: 2

#5: 1

--

public class SleepingThread extends Thread {

private int countDown = 5;

private static int threadCount = 0;

public SleepingThread() { … .start(); }

public void run() {

while(true) {

System.out.println(this);

if(--countDown == 0) return;

try {

sleep(100);// uspi na 0.1 sek.

} catch (InterruptedException e) { // výnimku musíme ochytiť

throw new RuntimeException(e); // spánok bol prerušený

}

}

}

public static voidmain(String[] args) throws InterruptedException {

for(int i = 0; i < 5; i++) {

new SleepingThread().join();// počkaj kým dobehne

System.out.println("--");

} }

}

Súbor: SleepingThread.java


Akanie na vl kno

Čakanie navlákno

  • nasledujúci príklad vytvorí 4 vlákna,

  • dva (Prvy, Druhy) triedy Sleeper, ktorý zaspia na 1.5 sek.

  • ďalšie dva (Treti, Stvrty) triedy Joiner, ktoré sa metódou join() pripoja na sleeperov a čakajú, kým dobehnú,

  • aby vedelo vlákno triedy Joiner, na koho má čakať, konštruktor triedy Joiner dostane odkaz na vlákno (sleepera), na ktorého má čakať,

  • medzičasom, výpočet vlákna Prvy násilne zastavíme v hlavnom vlákne metódou interrupt().

// hlavný thread:

Sleeper prvy = new Sleeper("Prvy", 1500);

Sleeper druhy = new Sleeper("Druhy", 1500),

Joiner treti = new Joiner("Treti", druhy),

Joiner stvrty = new Joiner("Stvrty", prvy);

prvy.interrupt();


Akanie na vl kno sleeper

Čakanie navlákno - Sleeper

class Joiner extends Thread {

private Sleeper sleeper;

public Joiner(String name, Sleeper sleeper) {

super(name);

this.sleeper = sleeper;

start();

}

public void run() {

try {

sleeper.join();

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

System.out.println(getName() + "dobehol");

}

}

class Sleeper extends Thread {

private int duration;

public Sleeper( String name,

int sleepTime) {

super(name);

duration = sleepTime;

start();

}

public void run() {

try {

sleep(duration);

} catch (InterruptedException e) {

System.out.println(getName() + " preruseny");

return;

}

System.out.println(getName() + " vyspaty");

}

}

Súbor: Sleeper.java


Akanie na vl kno joiner

Čakanie navlákno - Joiner

class Joiner extends Thread {

private Sleeper sleeper;

public Joiner(String name,

Sleeper sleeper) {

super(name);

this.sleeper = sleeper;

start();

}

public void run() {

try {

sleeper.join();

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

System.out.println(getName() + " dobehol");

}

}

class Sleeper extends Thread {

private int duration;

public Sleeper(String name, int sleepTime) {

super(name);

duration = sleepTime;

start();

}

public void run() {

try {

sleep(duration);

} catch (InterruptedException e) {

System.out.println(getName() + " preruseny");

return;

}

System.out.println(getName() + " vyspaty");

}

}

Prvy preruseny

Stvrty dobehol

Druhy vyspaty

Treti dobehol

Súbor: Joiner.java


Komunik cia medzi vl knami

Komunikácia medzi vláknami

  • doteraz sme mali príklady vlákien, ktoré medzi sebou (počas ich behu…) nekomunikovali (ak teda nerátame za komunikáciu, že sa zabíjali),

  • ak chceme, aby si vlákna vymieňali dáta, vytvoríme medzi nimi rúru (pipe),

  • rúra pozostáva z jednosmerne orientovaného streamu, ktorý sa na strane zapisovača (producenta, Sender) tvári ako PipedWriter, a na stranečítača (konzumenta, Reader) ako PipedReader,

  • aby čítač čítal z rúry, ktorú zapisovač pre neho vytvoril, musíme mu poslať odkaz na vytvorenú rúru PipedWriter, inak máme dve rúry...

  • do rúry možeme písať bajty, znaky, reťazce, objekty, v závislosti, ako si rúru zabalíme (viď techniky z I/O prednášky),

  • vytvoríme objekt Sender (producent), ktorý do rúry zapíše znaky A, B, ..., z

  • objekt Reader (konzument), ktorý číta znaky z rúry a vypíše A, B, ..., z

public class SenderReceiver {// hlavný program

public static void main(String[] args) throws Exception {

Sender sender = new Sender();

Receiver receiver = new Receiver(sender);

sender.start(); receiver.start();

}

}


V stupn r ra

Výstupná rúra

class Sender extends Thread {

private Random rand = new Random();

private PipedWriter out =

new PipedWriter(); // vytvor rúru na zápis, rúra je ukrytá, private

public PipedWriter getPipedWriter() {

return out; // daj rúru, bude ju potrebovať Reader na nadviazanie spojenia

}

public void run() {

while(true) {

for(char c = 'A'; c <= 'z'; c++) {

try {

out.write(c);// vypíš znaky abecedy do rúry

sleep(rand.nextInt(500));// a za každým počkaj max.½ sek.

} catch(Exception e) {

throw new RuntimeException(e);

}

}

}

Súbor: Sender.java


Vstupn r ra

Vstupná rúra

class Receiver extends Thread {

private PipedReader in;

public Receiver(Sender sender) throws IOException {

in = new PipedReader(sender.getPipedWriter());// vytvor vstupnú

}// rúru napojenú na výstupnú rúru Sendera

public void run() {

try {

while(true)// čítaj zo vstupnej rúry a píš na konzolu

System.out.println("Read: " + (char)in.read());

} catch(IOException e) {

throw new RuntimeException(e);

}

}

}

Read: A

Read: B

Read: C

Read: D

Read: E

Read: F

Read: G

Read: H

Read: I

Read: J

Read: K

Read: L

Read: M

Read: N

Read: O

Read: P

Read: Q

Read: R

Súbor: Receiver.java


Synchroniz cia

Synchronizácia

  • v prípade, ak dve vlákna zdieľajú nejaký zdroj, môže dôsť k nepredvídateľnej interakcii vlákien (napr. jeden číta, druhý píše),

  • spôsob, akým sa riadi prístup k zdieľaným zdrojom (synchronizácia) sa volá:

    • kritická sekcia,

    • semafór, mutex, PV operácie,

    • java monitor.

  • skúsime si sami naprogramovať semafór, aby sme pochopili, prečo táto vlastnosť musí byť súčasťou jazyka, a nie naprogramovaná v jazyku,

  • semafór reprezentuje celočíselná premenná semaphore inicializovaná na 0,

  • ak je zdieľaný zdroj voľný, semaphore == 0,

  • záujem použiť zdroj vyjadrím pomocou aquire(),

  • ak prestanem používať zdroj, uvoľním ho pomocou release().

  • Najivná implementácia vedie k tomu, že dve vlákna sa v istom čase dozvedia, že zdroj je voľný, oba si ho zarezervujú, a dochádza ku kolízii


Semaf r

Semafór

public class SemaphoreTester

extends Thread {

public void run() {

while(true)

if(semaphore.available()) {

yield();// skôr to spadne 

semaphore.acquire();

yield();

semaphore.release();

yield();

}

}

public static void main(String[] args)

throws Exception {

Semaphore sem = new Semaphore();

new SemaphoreTester(sem);

new SemaphoreTester(sem);

}

}

public class Semaphore {

// neoptimalizuj !

private volatile int semaphore = 0;

// môžem vojsť ?

public boolean available() {

return semaphore == 0;

}

// idem dnu !

public void acquire() { ++semaphore; }

// odchádzam...

public void release() {

--semaphore; }

}


Synchronizovan met da

Synchronizovaná metóda

Riešenie: Java ponúka konštrukciu synchronized:

  • synchronizovaná metóda– nie je možné súčasne volať dve synchronizované metódy toho istého objektu (kým sa vykonáva jedna synchronizovaná, ostatné sú pozastavené do jej skončenia).

    public class SynchronizedSemaphore extends Semaphore {

    private volatile int semaphore = 0;

    public synchronized boolean available() { return semaphore == 0; }

    public synchronizedvoid acquire() { ++semaphore; }

    public synchronizedvoid release() { --semaphore; }

    ... a teraz to už pojde ?

    public void run() {

    while(true)

    if(semaphore.available()) {

    semaphore.acquire();

    semaphore.release();

    }

    }


Synchronizovan k ritick sekcia

Synchronizovaná (kritická) sekcia

Atomická operácia:

  • sú operácie, ktoré sú nedeliteľné pre plánovač vlákien, napr. nie je možné, aby jedno vlákno zapísalo len spodné 2 bajty do premennej int,

  • čítanie a zápis do premenných primitívnych typov a premenných deklarovaných ako volatile je atomická operácia.

    ale

  • operácie nad zložitejšími štruktúrami nemusia byť synchronizované (napr. ArrayList, HashMap, LinkedList, … (v dokumentácii nájdete Note that this implementation is not synchronized).

    Riešenie:

    synchronizovaná sekcia – správa sa podobne ako synchronizovaná metóda, ale musí špecifikovať objekt, na ktorý sa synchronizácia vzťahuje.

  • while(true)

  • synchronized(this) {

  • if(semaphore.available()) {

    • semaphore.acquire();

  • semaphore.release();

  • }


Nesynchronizovan pr stup

Nesynchronizovaný prístup

Iný, praktickejší príklad dátovej štruktúry, ku ktorej nesynchronizovane pristupujú (modifikujú ju) dve vlákna:

publicclass ArrayListNotSynchronized extends Thread {

ArrayList<Integer> al = new ArrayList<Integer>();// štruktúra

int counter = 0; // počítadlo

//not synchronized

publicvoid add() {

System.out.println("add "+counter);

al.add(counter); counter++;// pridaj prvok do štruktúry

}

//not synchronized

publicvoid delete() {

if (al.indexOf(counter-1) != -1) { // nachádza sa v štruktúre

System.out.println("delete "+(counter-1));

al.remove(counter-1); counter--; // vyhoď zoštruktúry

}

}

}

Súbor: ArrayListNotSynchronized .java


Pokra ovanie dve vl kna

Pokračovanie – dve vlákna

Vlákno t1 pridáva prvky, vlákno t2 maže zo štruktúry

publicclass ArrayListThread extends Thread {

boolean kind;

static ArrayListNotSynchronized al = new ArrayListNotSynchronized();

public ArrayListThread(boolean kind) { this.kind = kind; }

publicvoid run() {

while (true) {

if (kind)

al.add();

else

al.delete();

}

}

publicstaticvoid main(String[] args) {

ArrayListThread t1 = new ArrayListThread(true); t1.start();

ArrayListThread t2 = new ArrayListThread(false); t2.start();

}

}

  • … a dostaneme (keď zakomentujeme System.out.println):

  • Exception in thread "Thread-2" java.lang.IndexOutOfBoundsException: Index: 17435, Size: 17432

  • at java.util.ArrayList.RangeCheck(Unknown Source)

  • at java.util.ArrayList.remove(Unknown Source)

  • at ArrayListNotSynchronized.delete(ArrayListNotSynchronized.java:15)

  • at ArrayListThread.run(ArrayListThread.java:12)

Súbor: ArrayListThread.java


Synchronizovan met da vs trukt ra

Synchronizovaná metóda vs. štruktúra

publicclass ArrayListNotSynchronized extends Thread {

ArrayList<Integer> al = new ArrayList<Integer>();

int counter = 0;

synchronizedpublicvoid add() {al.add(counter); counter++;}

synchronizedpublicvoid delete() {

if (al.indexOf(counter-1) != -1) { al.remove(counter-1); counter--;}

}

publicclass ArrayListSynchronized extends Thread {

List al = Collections.synchronizedList(new ArrayList());

int counter = 0;

publicvoid add() { al.add(counter); counter++; }

publicvoid delete() {

if (al.indexOf(counter-1) != -1) { al.remove(counter-1); counter--; }

}

}

Súbory: ArrayListNotSynchronized .java, ArrayListSynchronized .java


Monitor a akacia listina

Monitor a čakacia listina

Každý objekt má monitor, ktorý obsahuje jediné vlákno v danom čase. Keď sa vstupuje do synchronizovanej sekcie/metódy viazanej na tento objekt, vlákno sa poznačí v monitore. Ak sa opäť pokúša vlákno dostať do synchronizovanej sekcie, monitor už obsahuje iné vlákno, preto je vstup do sekcie pozastavený, kým toto neopustí sekciu (a monitor sa uvoľní).

Každý objekt má čakaciu listinu – tá obsahuje vlákna uspané prostredníctvom volania objekt.wait(), ktoréčakajú, kým iné vlákno prebudí tento objekt prostredníctvom objekt.notify().

public class Semaphore{

private int value;

public Semaphore(int val) {

value = val; }

public synchronized void release() {

++value;

notify();// this.notify();

}

}

public synchronized void acquire() {

while (value == 0)

try {

wait();// this.wait();

} catch (InterruptedException ie) { }

value--;

}

java.util.concurrent.Semaphor


Ohrani en buffer

Ohraničený buffer

Príklad: producer-consumer:

// zapíš objekt do buffra

public synchronized void put(Object o) throws InterruptedException {

while (count==size) wait();// kým je buffer plný, čakaj...

buf[in] = o;

++count;

in=(in+1) % size;

notify();// keď si zapísal, informuj čakajúceho

}

// vyber objekt do buffra

public synchronized Object get() throws InterruptedException {

while (count==0) wait();// kým je buffer prázdny, čakaj...

Object o =buf[out];

buf[out]=null;

--count;

out=(out+1) % size;

notify();// keď si vybral prvok, informuj ...

return (o);

}

Zdroj: http://www.doc.ic.ac.uk/~jnm/book/book_applets/concurrency.html


Stavy vl kna

Stavy vlákna

  • new – nenaštartovaný ešte,

  • runnable – može bežať, keď mu bude pridelený CPU,

  • dead – keď skončí metóda run(), resp. po stop(),

  • blocked – niečo mu bráni, aby bežal:

    • sleep(miliseconds) – počká daný čas, ak nie je interrupted...

    • wait(), resp. wait(milisec) čaká na správu notify() resp. notifyAll() ,

    • čaká na I/O,

    • pokúša sa zavolať synchronized metódu.

      sleep vs. wait

      keď proces volá wait(), výpočet je pozastavený, ale iné synchronizované metódy môžu byt volané


Ve eraj ci filozofovia

Večerajúci filozofovia

class Fork {

private boolean taken=false;

private PhilCanvas display;

private int identity;

Fork(PhilCanvas disp, int id) {

display = disp; identity = id;}

synchronized void put() {

taken=false;

display.setFork(identity,taken);

notify();

}

synchronized void get() throws java.lang.InterruptedException {

while (taken) wait();

taken=true;

display.setFork(identity,taken);

}

}

Súbor: Fork.java

Zdroj: http://www.cse.psu.edu/~catuscia/teaching/cg428/Concurrency_applets/concurrency/diners/


Ve eraj ci filozofovia1

3

2

2

1

3

4

1

4

0

0

Večerajúci filozofovia

class Philosopher extends Thread {

private PhilCanvas view;

. . . .

public void run() {

try {

while (true) {// thinking

view.setPhil(identity,view.THINKING);

sleep(controller.sleepTime());// hungry

view.setPhil(identity,view.HUNGRY);

right.get();// gotright chopstick

view.setPhil(identity,view.GOTRIGHT);

sleep(500);

left.get();// eating

view.setPhil(identity,view.EATING);

sleep(controller.eatTime());

right.put();

left.put();

}

} catch (java.lang.InterruptedException e){}

}

}

Súbor: Philosopher.java

Zdroj: http://www.cse.psu.edu/~catuscia/teaching/cg428/Concurrency_applets/concurrency/diners/


Ve eraj ci filozofovia2

http://www.doc.ic.ac.uk/~jnm/book/book_applets/Diners.html

Večerajúci filozofovia

Phil 0 thinking

Phil 0 has Chopstick 0 Waiting for Chopstick 1

Phil 0 eating

Phil 0 thinking

Phil 0 has Chopstick 0 Waiting for Chopstick 1

Phil 0 eating

Phil 0 thinking

Phil 0 has Chopstick 0 Waiting for Chopstick 1

Phil 0 eating

Phil 0 thinking

Phil 0 has Chopstick 0 Waiting for Chopstick 1

Phil 0 eating

Phil 0 thinking

Phil 0 has Chopstick 0 Waiting for Chopstick 1

Phil 0 eating

Phil 0 thinking

Phil 0 has Chopstick 0 Waiting for Chopstick 1

Phil 1 thinking

Phil 2 thinking

Phil 3 thinking

Phil 4 thinking

Phil 1 has Chopstick 1 Waiting for Chopstick 2

Phil 2 has Chopstick 2 Waiting for Chopstick 3

Phil 3 has Chopstick 3 Waiting for Chopstick 4

Phil 4 has Chopstick 4 Waiting for Chopstick 0

for (int i =0; i<N; ++i)

fork[i] = new Fork(display,i);

for (int i =0; i<N; ++i){

phil[i] = new Philosopher

(this,i,fork[(i-1+N)%N],fork[i]);

phil[i].start();

}


Pou en ve eraj ci filozof

Poučenývečerajúci filozof

class Philosopher extends Thread {

private PhilCanvas view;

. . . .

public void run() {

try {

while (true) {// thinking

view.setPhil(identity,view.THINKING);

sleep(controller.sleepTime());// hungry

view.setPhil(identity,view.HUNGRY);

if (identity%2 == 0) {

left.get();// gotleft chopstick

view.setPhil(identity,view.GOTLEFT);

} else {

right.get();// gotright chopstick

view.setPhil(identity,view.GOTRIGHT);

}

sleep(500);

if (identity%2 == 0)

right.get();// eating

else

left.get();// eating

view.setPhil(identity,view.EATING);

sleep(controller.eatTime());

right.put();

left.put();

}

} catch (java.lang.InterruptedException e){}

}

}

Súbor: FixedPhilosopher.java

Zdroj: http://www.cse.psu.edu/~catuscia/teaching/cg428/Concurrency_applets/concurrency/diners/


  • Login