Objektorienterad Realtidsprogrammering
2000
Föreläsning 5
Lite mer om synkronisering.
Mer om Java och trådar.
RMI
previous
next
Synkronisering. Java. RMI.
Exempel:visualisering av partiklar [ur Lea]
• Givet följande exempel, hittar vi två kritiska regioner
class Particle {
protected int x; protected int y; protected final Random rng = new Random();
public Particle(int initialX, int initialY) {
x = initialX; y = initialY;
}
Lösning
public void move() {
x += rng.nextInt(10) - 5;
y += rng.nextInt(20) - 10;
}
Kritiska
regioner
public void draw(Graphics g) {
int lx, ly;
lx = x;
ly = y;
g.drawRect(lx, ly, 10, 10);
}
}
previous
next
skriv så här:
public synchronized void move() {
x += rng.nextInt(10) - 5;
y += rng.nextInt(20) - 10;
}
public void draw(Graphics g) {
int lx, ly;
synchronized (this) {
lx = x;
ly = y;
}
g.drawRect(lx, ly, 10, 10);
}
2
Synkronisering. Java. RMI.
...
• Utan synkronisering
– så kan tex ett gammalt lx och ett nytt ly användas ihop
• vilket ju ger felaktigt resultat
• Med synkronisering
– Så skyddas dom kritiska regionerna
• Vad synkroniseriar vi då?
– Enkel regel
• Tre olika saker att synkronisera [ur Lea s 6]
– Lås alltid vid uppdateringar av objektets fält
– Lås alltid vid access av fält som kan förändras
– Lås aldrig då meddelanden skickas till andra objekt
previous
next
3
Synkronisering. Java. RMI.
...
• Resten av exemplet illustrerar
–
–
–
–
previous
next
hur vi kan definera ett grafiskt fönster
hur vi kan skriva en applet
mer om hur trådar kan användas
lite mer Javaspecifika saker som hur anonyma klasser och
grafiska objekt kan konstrueras
4
Synkronisering. Java. RMI.
Vi börjar med att definera den del som ritar partiklarna på fönsterytan
class ParticleCanvas extends Canvas {
private Particle[] particles = new Particle[0];
ParticleCanvas(int size) {
setSize(new Dimension(size, size));
}
// Intended to be called by applet
synchronized void setParticles(Particle[] ps) {
if (ps == null)
throw new IllegalArgumentException("Cannot set null");
particles = ps;
}
protected synchronized Particle[] getParticles() {
return particles;
}
previous
next
5
Synkronisering. Java. RMI.
...
public void paint(Graphics g) { // override Canvas.paint
Particle[] ps = getParticles();
for (int i = 0; i < ps.length; ++i)
ps[i].draw(g);
}
}
previous
next
6
Synkronisering. Java. RMI.
Sen appleten med flera trådar
class ParticleApplet extends Applet {
protected Thread[] threads; // null when not running
protected final ParticleCanvas canvas = new ParticleCanvas(100);
Då appleten
startar, init()
public void init() { add(canvas); }
protected Thread makeThread(final Particle p) { // utility
Runnable runloop = new Runnable() {
public void run() {
try {
Definition av anonym
for(;;) {
subklass till Runnable
p.move();
här emellan
canvas.repaint();
Thread.sleep(100); // 100msec is arbitrary
}
}
catch (InterruptedException e) { return; }
}
};
return new Thread(runloop);
}
previous
next
7
Synkronisering. Java. RMI.
...
public synchronized void start() {
int n = 10; // just for demo
if (threads == null) { // bypass if already started
Particle[] particles = new Particle[n];
for (int i = 0; i < n; ++i)
particles[i] = new Particle(50, 50);
canvas.setParticles(particles);
threads = new Thread[n];
for (int i = 0; i < n; ++i) {
threads[i] = makeThread(particles[i]);
threads[i].start();
}
}
}
previous
next
8
Synkronisering. Java. RMI.
...
public synchronized void stop() {
if (threads != null) { // bypass if already stopped
for (int i = 0; i < threads.length; ++i)
threads[i].interrupt();
threads = null;
}
}
}
previous
next
9
Synkronisering. Java. RMI.
Ömsesidig uteslutning
• Ett vanligt problem är att flera processer behöver dela en resurs
• Typiskt skriver en process och en annan läser vad som skrivs (då
det finns något att läsa)
• För att åstadkomma detta så behöver vi sätt att kommunicera
mellan processerna
– Fast vi vill inte ha några direkta referenser dem emellan
• En process skall kunna vänta på förändring medan en annan
meddelar om att sådan skett
• Jmf mönstret Observer där ett objekt meddelar (publicerar) att
förändring skett till beroende objekt (prenumeranter)
previous
next
10
Synkronisering. Java. RMI.
Kommunicera mellan trådar i Java
• Som vi såg på F4 kan vi använda wait and notify
för att vänta på respektive meddela om att en viss resurs
är tillgänglig
– wait()
• Vänta ända till notifikation sker (dvs ingen tidsgräns)
– wait(milliseconds)
• Vänta till notifikation sker men vänta max givet antal millisekunder
– notify()
• Meddela precis en (slumpvis utvald) väntande process
– notifyAll()
• Meddela alla väntande processer (i slumpvis ordning)
previous
next
11
Synkronisering. Java. RMI.
Exempel: tre trådar som skriver på terminalen
class MyThreadNotifyer2 extends Thread {
Object toNotify;
public MyThreadNotifyer2(Object t) {
toNotify = t;
}
public void info() {
synchronized(toNotify) {
toNotify.notifyAll();
Vänta max 200 ms
System.out.println(".");
try{toNotify.wait(200);} catch(InterruptedException e) {};
}
}
public void run() {
for(int i = 1; i < 21; i++) {
info();
}
}
}
previous
next
12
Synkronisering. Java. RMI.
...
public class MyThreadTest2 extends Thread {
int start = 0;
Object sync;
public MyThreadTest2(int s, Object sync) {
start = s;
this.sync = sync;
}
public void info(int i) {
synchronized(sync) {
try{sync.wait();}
catch(InterruptedException e) {};
System.out.println(i);
}
}
public void run() {
for(int i = start; i < (start + 10); i++) {
info(i);
}
}
previous
next
13
Synkronisering. Java. RMI.
...
public static void main(String[] args) {
Object sync = new Object();
MyThreadTest2 myThread1 = new MyThreadTest2(1, sync);
MyThreadTest2 myThread2 = new MyThreadTest2(100, sync);
MyThreadNotifyer2 notifier = new MyThreadNotifyer2(sync);
myThread1.start();
myThread2.start();
notifier.start();
//Vi väntar på en inmatning från terminalen
try{System.in.read();} catch(Exception e) {}
}
}
previous
next
14
Synkronisering. Java. RMI.
Exempel: producer-consumer
Producer
public class Producer extends Thread {
private CubbyHole cubbyhole;
private int number;
public Producer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
for (int i = 0; i < 10; i++) {
cubbyhole.put(i);
System.out.println("Producer #" + this.number
+ " put: " + i);
try {
sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
}
}
previous
next
15
Synkronisering. Java. RMI.
...
public class Consumer extends Thread {
private CubbyHole cubbyhole;
Consumer
private int number;
public Consumer(CubbyHole c, int number) {
cubbyhole = c;
this.number = number;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
value = cubbyhole.get();
System.out.println("Consumer #" + this.number
+ " got: " + value);
}
}
}
previous
next
16
Synkronisering. Java. RMI.
...
Delad
resurs
public class CubbyHole {
private int contents;
private boolean available = false;
public synchronized int get() {
while (available == false) {
try {
wait();
} catch (InterruptedException e) { }
}
available = false;
notifyAll();
return contents;
}
previous
next
17
Synkronisering. Java. RMI.
...
public synchronized void put(int value) {
while (available == true) {
try {
wait();
} catch (InterruptedException e) { }
}
contents = value;
available = true;
notifyAll();
}
}
previous
next
18
Synkronisering. Java. RMI.
…”huvudprogram”
public class ProducerConsumerTest {
public static void main(String[] args) {
CubbyHole c = new CubbyHole();
Producer p1 = new Producer(c, 1);
Consumer c1 = new Consumer(c, 1);
p1.start();
c1.start();
}
}
previous
next
19
Synkronisering. Java. RMI.
Semaforer
• En semafor är en mekanism för att erbjuda inbördes uteslutning
• Semaforer utvecklades först av E.W. Dijkstra under mitten av 60-talet
• En semafor är en delad heltalsvariabel som efter initieringen enbart kan
accessas via signal och wait (eller motsvarande namn)
– wait-metoden kontrollerar om heltalsvärdet är positivt och i så fall minskar den
med ett annars sätta anropande process att vänta
– signal-metoden kontrollerar om processer väntar på den i så fall får en av dem
fortsätta annars ökas värdet av semaforen med ett
• Implementationsskiss
– wait(): if s > 0 then s := s - 1
else suspend calling process
– signal(): if processes are waiting then wake up process
else s := s + 1
previous
next
20
Synkronisering. Java. RMI.
Semafor i Java
class Semaphore implements Sync
{
protected long permits; // current number of available permits
public Semaphore(long initialPermits) {
permits = initialPermits;
}
public synchronized void release() {
++permits;
notify();
}
previous
next
21
Synkronisering. Java. RMI.
...
public void acquire() throws InterruptedException {
if (Thread.interrupted()) throw new
InterruptedException();
synchronized(this) {
try {
while (permits <= 0) wait();
--permits;
}
catch (InterruptedException ie) {
notify();
throw ie;
}
}
}
previous
next
22
Synkronisering. Java. RMI.
...en metod med time out på väntandet
public boolean attempt(long msecs)throws InterruptedException{
if (Thread.interrupted()) throw new InterruptedException();
synchronized(this) {
if (permits > 0) {
// Same as acquire but messier
--permits;
return true;
}
else if (msecs <= 0)
// avoid timed wait if not needed
return false;
else {
try {
long startTime = System.currentTimeMillis();
long waitTime = msecs;
Forts nästa sida
previous
next
23
Synkronisering. Java. RMI.
...
for (;;) {
wait(waitTime);
if (permits > 0) {
--permits;
return true;
}
else {
// Check for time-out
long now = System.currentTimeMillis();
waitTime = msecs - (now - startTime);
if (waitTime <= 0)
return false;
}
}
}
catch(InterruptedException ie) {
notify();
throw ie;
}
}
}
}
}
previous
next
24
Synkronisering. Java. RMI.
Olika typer av semaforer, tex
• Blocked-Set
– Signal väcker en godtycklig väntande process
• Blocked-Queue
– Signal väcker i FIFO-ordning
• Blocked-Priority
– Väcker den med högst prioritet (om lika i FIFO-rdning)
previous
next
25
Synkronisering. Java. RMI.
Exempel: bounded buffer med semafor
class BufferArray {
protected final Object[] array;
// the elements
protected int putPtr = 0;
// circular indices
protected int takePtr = 0;
BufferArray(int n) { array = new Object[n]; }
synchronized void insert(Object x) { // put mechanics
array[putPtr] = x;
putPtr = (putPtr + 1) % array.length;
}
synchronized Object extract() {
// take mechanics
Object x = array[takePtr];
array[takePtr] = null;
takePtr = (takePtr + 1) % array.length;
return x;
}
}
previous
next
26
Synkronisering. Java. RMI.
...
class BoundedBufferWithSemaphores {
protected final BufferArray buff;
protected final Semaphore putPermits;
protected final Semaphore takePermits;
public BoundedBufferWithSemaphores(int capacity)
throws IllegalArgumentException {
if (capacity <= 0) throw new IllegalArgumentException();
buff = new BufferArray(capacity);
putPermits = new Semaphore(capacity);
takePermits = new Semaphore(0);
}
public void put(Object x) throws InterruptedException {
putPermits.acquire();
buff.insert(x);
takePermits.release();
}
previous
next
27
Synkronisering. Java. RMI.
...
public Object take() throws InterruptedException {
takePermits.acquire();
Object x = buff.extract();
putPermits.release();
return x;
}
public Object poll(long msecs) throws InterruptedException {
if (!takePermits.attempt(msecs)) return null;
Object x = buff.extract();
putPermits.release();
return x;
}
public boolean offer(Object x, long msecs throws InterruptedException {
if (!putPermits.attempt(msecs)) return false;
buff.insert(x);
takePermits.release();
return true;
}
}
previous
next
28
Synkronisering. Java. RMI.
Channel
class SynchronousChannel /* implements Channel */ {
protected Object item = null; // to hold while in transit
protected final Semaphore putPermit;
protected final Semaphore takePermit;
protected final Semaphore taken;
public SynchronousChannel() {
putPermit = new Semaphore(1);
takePermit = new Semaphore(0);
taken = new Semaphore(0);
}
previous
next
29
Synkronisering. Java. RMI.
...
public void put(Object x) throws InterruptedException {
putPermit.acquire();
item = x;
takePermit.release();
// Must wait until signalled by taker
InterruptedException caught = null;
for (;;) {
try {
taken.acquire();
break;
}
catch(InterruptedException ie) { caught = ie; }
}
if (caught != null) throw caught; // can now rethrow
}
previous
next
30
Synkronisering. Java. RMI.
...
public Object take() throws InterruptedException {
takePermit.acquire();
Object x = item;
item = null;
putPermit.release();
taken.release();
return x;
}
}
previous
next
31
Synkronisering. Java. RMI.
Prioritering av processer
• Vi kan definiera processer med olika prioritet
– En process med högre prioritet går före en process med lägre
prioritet
• Preemptive
– Att processer med högre prioritet får exekvera före och kan
avbryta processer med lägre prioritet kallas för ”preemptive”
• I Java kan en tråds prioritet sättas med
setPriority(int newPriority)
– där newPriority  [1, 10]
• se Suns tutorial för exempel
previous
next
32
Synkronisering. Java. RMI.
Prioritetsinvertering
• Problem: En process med lägre prioritet kan blockera en process
med högre prioritet
• Orsak: En process med lägre prioritet, plow, kan ha fått tillgång till
en resurs (icke preemptive tex fil eller skrivare) en process med
lite högre prioritet, pmedium, vill exekvera och får det samtidigt som
en annan process, phigh, vill exekvera och ha tillgång till resursen
som, plow, håller.
Dvs phigh avbryter och väntar på samma resurs som plow har. Vilket
resulterar att pmedium får exekvera och plow kan inte släppa resursen
phigh
pmedium
plow
Botemedel Vid blockering av phigh så höjs prioriteten för den process som har
resursen till phigh:s prioritet
previous
next
33
Distribuerad programmering med Javas
RMI
RMI, Remote Method Invocation
previous
next
Synkronisering. Java. RMI.
Remote Method Invocation, RMI
• Distributionspaketet RMI introducerades i Java 1.1
• RMI gör det enkelt att dela på objekt mellan olika
plattformar
– objekt i en maskin kan transparent anropa objekt tillhörande
andra maskiner
– kompilerad kod kan flyttas och exekvera i olika virtuella
maskiner
• RMI kräver dock att både klient och server kör Java
previous
next
35
Synkronisering. Java. RMI.
Vad behöver göras för att distribuera objekt?
1 Skapa gränssnitt för objekt som skall kunna anropas av objekt på
andra maskiner.
– Detta gränssnitt (interface) används av både server och klient
– interfacet skall utvidga java.rmi.Remote
– varje metod i interfacet skall deklarera att det kastar
java.rmi.RemoteException
2 Vi behöver skapa en klient
– Som använder objektet på servern
3 Ett objekt som implementerar gränssnittet i steg 1
4 En server som skapar en instans av det distribuerade objektet
(som också implementerar samma gränssnitt som objektets
representation hos klienten)
previous
next
36
Synkronisering. Java. RMI.
Hur representeras objektet hos server respektive klient?
• Hos servern skapas en vanlig instans som implementerar det distribuerade
gränssnittet
• Då man (via namnservern) "ber om" en instans av det distribuerade objektet
hos klienten skickas en stubbe från servern till klienten
• Hos klienten skapas en proxy som från klientens sida ser ut som om objektet
befann sig på dess lokala maskin (dvs det ser ut som ett vanligt objekt som
implementerar det givna "distributionsgränssnittet")
• Proxyn ansvarar för att på ett transparent sätt vidarebefordra meddelanden från
klient till det riktiga objektet på servern samt att returnera resultatet tillbaks till
anroparen
– Dvs klienten skall inte kunna skilja ett meddelande till objektet på servern från ett
meddelande till ett "vanligt" lokalt objekt
previous
next
37
Synkronisering. Java. RMI.
Transport av seriealiserat objekt från klient till server
• Ett litet exempel som visar hur vi kan konstruera en klass och ett objekt på
klientsidan och skicka det till en server för exekvering
• Konstruktion av interface för objektet som skall implementeras på servern och
anropas av klienten
import java.rmi.*;
Objekt som
kan anropas
av annan nod
interface HelloWithTransport extends Remote {
public void sayHelloVia(HelloTransportInterface
transportObject)
throws java.rmi.RemoteException;
}
• Interface för objekt som skall kunna transporteras
Objekt som
kan flyttas
import java.io.Serializable;
interface HelloTransportInterface extends
Serializable{
public void say(String saying);
}
previous
next
38
Synkronisering. Java. RMI.
Konstruktion av Server...
import java.rmi.*;
import java.rmi.server.*;
Subklassa
UnicastRemoteObject
och implementera det egna
HelloWithTransport
public class HelloServerTransport extends UnicastRemoteObject
implements HelloWithTransport {
public HelloServerTransport() throws RemoteException {
super();
Superklassens
}
konstruktör
och metoden som definieras
ser till att
i HelloWithTransport
"vår"
som sen kommer användas
instans
av klienter
exporteras
public void sayHelloVia(HelloTransportInterface
transportObject) throws RemoteException {
transportObject.say("Hello Internetprogrammers (via
transport)!");
}
previous
next
39
Synkronisering. Java. RMI.
...
public static void main(String [] args) {
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
HelloServerTransport h = new HelloServerTransport();
Skapa instans
Naming.rebind(”//host/hellowithtransport", h);
Ge "distribuerat" namn
System.out.println("Hello with Callback Server
ready.");
}
catch(RemoteException re) {
System.out.println("RemoteException in
HelloServerTransport.main: " + re);
}
catch(Exception e) {
System.out.println("Exception in
HelloServerTransport.main: " + e);
}}}
previous
next
40
Synkronisering. Java. RMI.
Klient
import java.rmi.*;
import java.io.*;
public class HelloClientWithTransport {
public static class MyTransportObject implements
HelloTransportInterface {
public void say(String saying){
System.out.println("From the client: " + saying);
}
}
public static void main(String [] args) {
System.out.println("Client started");
if (System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
try {
HelloWithTransport
h = (HelloWithTransport)
Skapa
Naming.lookup(”//host/hellowithtransport");
referens till
servern
previous
next
41
Synkronisering. Java. RMI.
...
//Transportera koden för ett objekt och exekvera hos servern.
MyTransportObject
transport = new MyTransportObject ();
Skapa instans
(av klienten)
h.sayHelloVia(transport);
}
Anropa metod
hos servern
med klienten som
argument
catch(Exception e) {
System.out.println("Exception in
HelloClientWithTransport .main: " + e);
}
}}
previous
next
42
Synkronisering. Java. RMI.
Kompilera
• Kompilera båda interfacen, servern och klienten med javac,
dvs
>javac HelloWithTransport.java
>javac HelloTransportInterface.java
>javac HelloServerTransport.java
>javac HelloClientWithTransport.java
• Kör också rmic på servern, dvs
>rmic HelloServerTransport
previous
next
43
Synkronisering. Java. RMI.
Testkörning
Vi kör exemplet på följande sätt (svar i kursivt):
• Starta namnserver
>rmiregistry
• Starta server
>java HelloServerTransport
Hello with Callback Server ready.
• Starta klient
>java HelloClientWithTransport
Client started
Iockmed att klienten startas kommer också
följande skrivas på severns terminal:
From the client: Hello Internetprogrammers
(via transport)!
previous
next
44
Synkronisering. Java. RMI.
Definiera, kompilera och kör
• Kortfattat gör på följande sätt
– Skapa interface
– Kompilera interfacet med javac InterfaceNamn.java
– Skapa server som instansierar serverobjekt och binder det till namnservern
– Kompilera servern med javac ServerKlass.java
– Kör rmic på serverklassen (så skapas stubbe och skelettklasser), dvs
rmic ServerKlass
– Skriv klient som refererar objekt i namnservern och skickar meddelanden
till det
– Kompilera klienten (javac KlientKlass.java)
– Starta namnservern (rmiregistry)
– Starta server (java ServerKlass)
– Starta klint (Java KlientKlass)
previous
next
45
Synkronisering. Java. RMI.
RMI mer beskrivningar och fler exempel
Läs också gärna tutorialen som du hittar på följande adress:
http://www.javasoft.com/docs/books/tutorial/rmi/index.html
Vi tittar också på dom tre exemplen på följande sida
http://www.nada.kth.se/kurser/kth/2D4334/99-00/contents/exempel.html
dvs följande:
• Transport av serialiserat objekt
• Calbackrutin
• Meddelandesystem
previous
next
46