200 likes | 372 Views
Concurrent Programming (Java Threads). Example 1 (Burma Shave). Concurrent Programming (Concerns). Mutual Exclusion - must be certain that concurrent processes have mutual exclusive access to shared data.
E N D
Concurrent Programming(Java Threads) Example 1 (Burma Shave)
Concurrent Programming(Concerns) • Mutual Exclusion - must be certain that concurrent processes have mutual exclusive access to shared data. • Race Condition - must avoid situations in which the relative speed of various processes affect the outcome. • Deadlock & Starvation - must avoid situations in which processes are placed in a locked condition indefinitely.
Multiple Threads A program can be divided into separate, independently running subtasks – each of which is called a thread. An underlying mechanism within the operating system is allocating the CPU time to each of the threads.
Create a new thread and start it running Creating Threads Method 1 – Inheriting from class Thread Override the method run() inherited from Thread. publicclass ThreadExample extends Thread { ………… public ThreadExample( ) {//implement the constructor} publicvoidrun( ) { while (true) { //loop forever until a stopping condition occurs ….. } } publicstaticvoid main(String [] args) { //do something then start the threads ThreadExample tseliot = new ThreadExample(); tseliot.start(); } The start() method in class Thread performs initialization of the thread and then calls run
The Thread constructor takes a Runnable parameter Method sleep can throw an exception Creating Threads Method 2 – Implementing Interface Runnable publicclass ThreadExample2 extends Applet implements Runnable { private Thread t = new Thread(this); ……. publicvoidrun( ) { while (true) { try{ t.sleep(1000); //wake up and do something } catch(InterruptedException e) {/*handle e*/} } } publicvoid init( ) { ….. t.start(); } }
Creating Threads The Runnable Interface used in the second example has the following features: • It provides a means for implementing multiple inheritance in Java • It requires that a (new) Thread be specifically created in the applet • The method run( ) is specified in the interface Runnable • (Note!) In Method 1 – where the application extended (inherited from) the class Thread, the run( ) method that the programmer needed to implement was present because Thread also implements interface Runnable. • start( ) is a method in Thread – the Thread object receives the message to perform this operation.
Inheriting from Thread Class publicclass BouncingBall extends Applet { …. privateclass BallThread extends Thread { publicvoid run ( ) { //Step 1 -- override method run in Thread while (true) { aBall.move(); if((aBall.x() < 0) || (aBall.x() > AppletWidth)) aBall.setMotion(-aBall.xMotion(), aBall.yMotion()); if((aBall.y() < 0) || (aBall.y() > AppletHeight)) aBall.setMotion(aBall.xMotion(), -aBall.yMotion()); repaint(); try { sleep(50); // inherited from Thread } catch (InterruptedException e) {} } } private ThreadedBallWorld (Color ballColor) { …. aBall = new Ball(10, 15, 5); aBall.setColor(ballColor); aBall.setMotion(3.0, 6.0); Thread ballThread = new BallThread(); ballThread.start( ); }
Starting a thread involves creating two objects—an instance of Runable which describes the actions to be performed, and a Thread object which schedules actions for execution. The Runable object is passed as an argument to the Thread. Implementing the Interface Runable publicclass BouncingBall extends Applet { …. privateclass BallThread implements Runable { publicvoid run ( ) { //override method run in Thread while (true) { aBall.move(); if((aBall.x() < 0) || (aBall.x() > AppletWidth)) aBall.setMotion(-aBall.xMotion(), aBall.yMotion()); if((aBall.y() < 0) || (aBall.y() > AppletHeight)) aBall.setMotion(aBall.xMotion(), -aBall.yMotion()); repaint(); try { Thread.sleep(50); // sleep is not automatically inherited } catch (InterruptedException e) {} } } private ThreadedBallWorld (Color ballColor) { …. aBall = new Ball(10, 15, 5); aBall.setColor(ballColor); aBall.setMotion(3.0, 6.0); Thread ballThread = new Thread(new BallThread); ballThread.start( ); }
5 5 5 8 8 8 10 10 10 P1 P1 P1 P2 P2 P2 P3 P3 P3 5] 23] 15] Critical Sections Object semaphore; //synchronized object to “lock” critical section Producer Threads synchronized (semaphore) { total += val; } Critical Section [total = 0] Synchronizing a method public synchronized void someMethod ( ) { body } OR public void someMethod ( ) { synchronized (this) { body } }
Semaphores publicclass Semaphore { private int s; public Semaphore(int sInit){ s = sInit; //initialized to the number of threads allowed into the critical section at one time } public Semaphore(){ s = 0; } publicsynchronizedvoid sWait() throws InterruptedException { while(s <= 0) try {this.wait();} catch(InterruptedException ignore){} --s; } publicsynchronized void sSignal() { ++s; this.notify(); } }
Producers and Consumers Multiple Producers generate (integer) data that is asynchronously placed in a container (such as a stack, queue, or vector). Multiple Consumers asynchronously retrieve data from the container. We want to ensure that each piece of data generated by a Producer is retrieved by only one of the Consumers. Container (Stack) • Potential Problems • Two or more producers write data to the same location -- (before the first producer updates the size of the container, the second producer reads the original size and overwrites the first entry. The size is updated by both producers, which means that an improper data element appears in the container.) -- eliminated by synchronizing the insert method • Two or more Consumers retrieve the same data element – (This will also cause the size of the container to be reduced twice when only one data item is extracted.) – eliminated by synchronizing the remove method • A Producer and a Consumer perform an insert and a remove at the same time and it becomes a race condition to see which completes first. – not prevented by separately synchronizing the two methodsseparately • A Consumer tests that the container is not empty as another Consumer is removing the last piece of data from it. – not prevented by synchronizing the two methodsseparately
Producers and Consumers • Need to allow only one producer or one consumer access to the container at a time. • Each data item produced (inserted) must be consumed (removed) before another is produced Semaphore notEmpty = new Semaphore(); Semaphore notFull = new Semaphore(containerSize); //declare two semaphores publicvoid insert(Object data) { try {notFull.sWait();} catch (InterruptedException e) {} {body of method} notEmpty.sSignal(); } public Object remove( ) { try {notEmpty.sWait();} catch(InterruptedException e) {} {body of method} notFull.sSignal(); } No! Are we Safe yet? More than one producer and/or consumer can be admitted to the critical section since the number of “tokens” managed by the semaphores will be equal to the capacity of the container. We need another control to ensure that only one “token holder” can actually enter the critical section at a time.
Producers and Consumers One additional barrier is needed to ensure that only one process is in the critical section at a time. Semaphore notEmpty = new Semaphore(); Semaphore notFull = new Semaphore(containerSize); Semaphore access = new Semaphore(1); publicvoid insert (Object item) { try {notFull.sWait();} catch (InterruptedException e) {} try {access.sWait();} catch (InterruptedException e) {} {body of the method} access.sSignal(); notEmpty.sSignal(); } public Object remove() { try {notEmpty.sWait();} catch (InterruptedException e) {} try {access.sWait();} catch (InterruptedException e) {} {body of the method} access.sSignal(); notFull.sSignal(); }
ObserverReporter Nine Observers each asynchronously increment a variable count 500 times. Nine Reporters asynchronously record the count and set it back to zero. The Reporters remain active while there are active Observers. Object Diagram ObserveReport0 No synchronization of the critical section. ObserveReport1 Methods incCount() and getCount() are separately synchronized.
ProducerConsumer A Producer generates integer values from 0 to 99 and places them in a queue of capacity 5. A Consumer asynchronously removes a value from the queue, determines its value, and increments a counter (one of 100) that indicates the number of times that the value associated with that counter has been retrieved. The Consumer remains active as long as the Producer is active and then as long as there are values remaining in the queue to be consumed. The Applet displays the count for each integer produced and the total number of counts made by the Consumer. ProducerConsumer1 No synchronization for enqueue() and dequeue() ProducerConsumer2 All queue methods synchronized separately. ProducerConsumer3 Same as #2 but with 2 Producers and 2 Consumers ProducerConsumer4 Same as #3 but a test is made to determine if the Queue is not full before an insert and not empty before a remove. ProducerConsumer5 Semaphores notFull, notEmpty, and access are used as previously explained.
Dining Philosophers Five philosophers sit around a round table with a bowl of spaghetti in the middle. The philosophers alternately talk and eat. There is a fork located between each of the philosophers. In order to eat a philosopher must acquire both the fork on his or her left and on his or her right. In this problem we need to devise a solution for sharing a set of resources so that deadlock and starvation are avoided.
Dining Philosophers Deadlock!
Dining Philosophers Starvation!
Dining Philosophers Dining Philosopher1 Semaphores on each fork allow only one Philosopher to have access at a time. All philosophers take the left fork first. Dining Philosopher2 Same as above, but one (odd) Philosopher adopts a different strategy –goes for the right fork first.