Download Java Threads ()

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Computational electromagnetics wikipedia , lookup

Scheduling (computing) wikipedia , lookup

Block sort wikipedia , lookup

Transcript
Java Threads
CS 350 – Fall 2001 – 11/29/01, updated 3/18/02
… Preliminary …
Java Threads (Reference: Silberschatz: page 122 …):
See instructors commentary notes on Java (on the web site).

Overview
Provides thread support at the language level
All java programs have at least one thread corresponding to the main method.
Other threads can be created using system provided classes in a program.
The primary focus will be on stand-alone java programs containing a main
method (as opposed to applets).
NOTE: Java threads exist in the “Java Virtual Machine” which is usually
implemented as a layer above the Operating System

Thread creation
There is a generic thread class provided by JDK from which all threads in an
application may be derived by extension.
There is a method in the thread class called run() which does nothing. In the
extended class, the run() method is defined and will over-ride the generic run()
in the thread class with the desired behavior of the thread being implemented,
ie., run() will be implemented in the extended class.
Calling the start() method in the derived thread (from a class in your program)
creates the new thread in the JVM. Allocation alone does not fire off the
thread. In addition to initializing the new thread, start will call run() which
will make the thread “do its thing” in the JVM. Note: do not call run directly –
let start() do it.
See and try the example in the example below (ref Fig. 5.7, Silberschatz):
Java Threads
Spring 2002
11/29/01
page 1
class Worker1 extends Thread
{
public void run() {
System.out.println("I Am a Worker Thread");
}
}
public class First
{
public static void main(String args[]) {
Worker1 runner = new Worker1();
runner.start();
System.out.println("I Am The Main Thread");
}
}
An alternate method of creating a thread is by implementing the interface
called Runnable provided by JDK. This interface class contains the nonimplemented run() method. The class containing main creates an object (call
it runner) from the implemented abstract class. Finally main allocates an object
for the thread class passing a reference to runner to its constructor. Main then
calls start() in this thread object. See fig 5.8 for an example:
class Worker2 implements Runnable
{
public void run() {
System.out.println(“I am a worker thread.
}
}
“);
public class Second
{
public static void main(String args[]) {
Runnable runner = new Worker2();
Thread thrd = new Thread(runner);
thrd.start();
System.out.println("I Am The Main Thread");
}
}
Java Threads
Spring 2002
11/29/01
page 2
 Thread Management
The thread class provides various methods for managing threads.
The following API’s are provided:
suspend() – suspends the execution of the currently running thread
sleep() – puts the current thread to sleep for a specified amount of time.
resume() – resumes execution of a suspended thread
see the clock applet example Fig 5.9:
/**
* ClockApplet.java
* This applet displays the time of day.
* Note that this program uses deprecated methods,
* an alternative version will be shown in chapter 8.
*/
import java.applet.*;
import java.awt.*;
public class ClockApplet extends Applet implements Runnable
{
public void run() {
while (true) {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {}
repaint();
}
}
// this method is called when the applet is started or
// we return to the applet
public void start() {
if ( clockThread == null ) {
clockThread = new Thread(this);
clockThread.start();
}
else
clockThread.resume();
}
// this method is called when we leave the page the applet is on
public void stop() {
if (clockThread != null)
clockThread.suspend();
}
// this method is called when the applet is removed from the cache
public void destroy() {
Java Threads
Spring 2002
11/29/01
page 3
if (clockThread != null) {
clockThread.stop();
clockThread = null;
}
}
public void paint(Graphics g) {
g.drawString( new java.util.Date().toString(), 10, 30);
}
private Thread clockThread;
}

Thread states:
Similar to processes, threads have the states:
New – the state on being created using the “new” statement in the code.
Runnable – when run() is called via the start() call, the thread is moved to the
runnable state. This means that the thread is eligible to be run in the JVM.
This state is a bit like the ready state for processes, except for one difference:
when a thread goes into active execution, it is still in the runnable state instead
a s distinct “running” state which a process would be in.
Blocked – a thread becomes blocked if it performs a blocking call such as I/O
or it does sleep() or suspend call.
Dead – A thread goes to the dead state when its run() method terminates or
when its stop() method is called.

Threads and the JVM/Operating system (see pp. 128-129)
Remember all java threads “live” in the JVM. Some JVM threads are for
system management, while others are user created in applications (sect 5.6.4).
Java Threads
Spring 2002
11/29/01
page 4
JVM is usually implemented on “top” of the host operatins system.
The JVM specification does not indicate how Java threads are mapped to
underlying the operating system (threads) – this is left to the particular
implementation.
Typically a Java thread is considered a user-level thread, and the JVM is
responsible for the thread management.
The various OS/User thread models previously discussed applies to Java
threads treated as user threads.

A multithreaded solution to the producer/consumer problem
See code given in both chapter 4 and 5 for the bounded buffer and “IPC”
solution, and in chapter7 for the bounded buffer semaphore solution .
Complete code is given in the author’s web site:
http://www.bell-labs.com/topic/books/aos-book/
Also see diagram below in this document for the description of classes used to
solve the shared memory or bounded buffer version of the problem (chapter 4).
Java Threads
Spring 2002
11/29/01
page 5
Java Threads
Spring 2002
11/29/01
page 6
Code for the multi-threaded “bounded buffer” solution as given in Chapter 4.
/**
* Server.java
* no-synch case – ch.4
* This creates the buffer and the producer and consumer threads.
*
* @author Greg Gagne, Peter Galvin, Avi Silberschatz
* @version 1.0 - July 15, 1999
* Copyright 2000 by Greg Gagne, Peter Galvin, Avi Silberschatz
* Applied Operating Systems Concepts - John Wiley and Sons, Inc.
*/
public class Server
{
public static void main(String args[]) {
BoundedBuffer server = new BoundedBuffer();
// now create the producer and consumer threads
Producer producerThread = new Producer(server);
Consumer consumerThread = new Consumer(server);
producerThread.start();
consumerThread.start();
}
}
Java Threads
Spring 2002
11/29/01
page 7
/**
* Producer.java
* no-synch case – ch.4
* This is the producer thread for the bounded buffer problem.
*
* @author Greg Gagne, Peter Galvin, Avi Silberschatz
* @version 1.0 - July 15, 1999
* Copyright 2000 by Greg Gagne, Peter Galvin, Avi Silberschatz
* Applied Operating Systems Concepts - John Wiley and Sons, Inc.
*/
import java.util.*;
public class Producer extends Thread
{
public Producer(BoundedBuffer b) {
buffer = b;
}
public void run()
{
Date message;
while (true)
{
int sleeptime = (int) (BoundedBuffer.NAP_TIME * Math.random() );
System.out.println("Producer sleeping for " + sleeptime + " seconds");
try { sleep(sleeptime*1000); }
catch(InterruptedException e) {}
// produce an item & enter it into the buffer
message = new Date();
System.out.println("Producer produced " + message);
buffer.enter(message);
}
}
private
BoundedBuffer buffer;
}
Java Threads
Spring 2002
11/29/01
page 8
/**
* Consumer.java
* no-synch case – ch.4
* This is the consumer thread for the bounded buffer problem.
*
* @author Greg Gagne, Peter Galvin, Avi Silberschatz
* @version 1.0 - July 15, 1999
* Copyright 2000 by Greg Gagne, Peter Galvin, Avi Silberschatz
* Applied Operating Systems Concepts - John Wiley and Sons, Inc.
*/
import java.util.*;
public class Consumer extends Thread
{
public Consumer(BoundedBuffer b)
{
buffer = b;
}
public void run()
{
Date message;
while (true)
{
int sleeptime = (int) (BoundedBuffer.NAP_TIME * Math.random() );
System.out.println("Consumer sleeping for " + sleeptime + " seconds");
try { sleep(sleeptime*1000); }
catch(InterruptedException e) {}
// consume an item from the buffer
System.out.println("Consumer wants to consume.");
message = (Date)buffer.remove();
}
}
private
BoundedBuffer buffer;
}
Java Threads
Spring 2002
11/29/01
page 9
/**
* BoundedBuffer.java
* no-synch case – ch.4
* This program implements the bounded buffer using shared memory.
* Note that this solutions is NOT thread-safe. It will be used
* to illustrate thread safety using Java synchronization in Chapter 7.
*
* @author Greg Gagne, Peter Galvin, Avi Silberschatz
* @version 1.0 - July 15, 1999
* Copyright 2000 by Greg Gagne, Peter Galvin, Avi Silberschatz
* Applied Operating Systems Concepts - John Wiley and Sons, Inc.
*/
import java.util.*;
public class BoundedBuffer
{
public BoundedBuffer()
{
// buffer is initially empty
count = 0;
in = 0;
out = 0;
buffer = new Object[BUFFER_SIZE];
}
// producer calls this method
public void enter(Object item) {
while (count == BUFFER_SIZE)
; // do nothing
// add an item to the buffer
++count;
buffer[in] = item;
in = (in + 1) % BUFFER_SIZE;
if (count == BUFFER_SIZE)
System.out.println("Producer Entered " + item + " Buffer FULL");
else
System.out.println("Producer Entered " + item + " Buffer Size = " +
count);
}
// consumer calls this method
public Object remove() {
Object item;
while (count == 0)
; // do nothing
// remove an item from the buffer
--count;
item = buffer[out];
out = (out + 1) % BUFFER_SIZE;
if (count == 0)
System.out.println("Consumer Consumed " + item + " Buffer EMPTY");
else
System.out.println("Consumer Consumed " + item + " Buffer Size = " + count);
return item;
}
public static final int
private static final int
private
private
private
private
NAP_TIME = 5;
BUFFER_SIZE = 3;
volatile int count;
int in;
// points to the next free position in the buffer
int out; // points to the next full position in the buffer
Object[] buffer;
}
Java Threads
Spring 2002
11/29/01
page 10
Java Synchronization
Based on “Applied Operating System Concepts”, 1st ed., by Silberschatz.
There are three mechanisms offered by the Java language for assisting in process
synchronization.
yield() method
If you use some form of busy wait to achieve synchronization, then to avoid wasting
CPU time, the yield() method could be called as the thread polls on some condition.
Yield() simply causes the calling thread to relinquish the CPU and voluntarily goes to
the ready queue. only threads of equal or higher priority can ruin in its place. This
has the potential of deadlock.
Use of the synchronized key word for methods:
Example:
public synchronized Object remove() { …}
Every object in Java has associated with it a single lock. If a method of an object is
declared with the synchronized key word, calling the method requires owning the
lock for the object. If the lock is already owned, the thread calling the synchronized
method blocks and is put in the entry set queue for the object’s lock. If the lock is
available when a synchronized method is called, the calling thread becomes the
owner of the lock, and it can enter the method. The lock is released when the thread
exits the method. If an entry set for the lock is not empty, when the lock is released,
the JVM selects an arbitrary thread from this set as the new owner of the lock. See
below:
... take the lock with you when entering the CS:
This technique is OK for mutual exclusion, except when the owner of a lock uses a
yield() polling loop to wait for an event to happen (for example, in the
producer/consumer problem, the producer sees a full queue, and goes into a yield
polling loop while holding the lock – see fig 7.28), the consume will never be able to
Java Threads
Spring 2002
11/29/01
page 11
consume anything to free up the producer because it does not have the lock. Thus we
have deadlock again. We need some way of having one thread signaling another
waiting thread when a condition occurs. This is achieved with the wait() and notify()
synchronization primitives.
Use of wait() and notify() along with the synchronized key word
In a method declared with the synchronized key word
In addition to having a lock and entry set, every object also has associated with if a
wait set (of threads which is initially empty). When a thread enters a synchronized
method, it owns the lock for the object. However, this thread may determine that it is
unable to continue because a certain condition is not met (example, buffer full for
producer). In this case the thread calls the wait() method, with the following
happening:
1.
2.
3.
The thread releases the lock for the object.
The state of the thread is set to Blocked.
The thread is placed in the wait set for the object.
When some other thread calls notify(), indicating the condition the first thread was
waiting or has taken place, we have the following events. The call to notify():
1.
2.
3.
Picks an arbitrary thread T from he list of threads in the wait set.
Moves T from the wait set to the entry set.
Sets the state of T from Blocked to Runnable (ready).
T is now eligible to compete for the lock with the other threads. Once T has regained
control of the lock, it returns from calling wait(), where it may check the condition
that cause the block again. See below:
Java Threads
Spring 2002
11/29/01
page 12
See the source code for the bounded buffer problem (producer/consumer), for an
illustration of the wait()/Notify() synchronization techniques (fig. 7.30 or {7.32, 7.33,
7.34}).
Comparison of java Wait/notify with semaphore wait/signal:
wait() and notify() resemble the wait(sem) and signal(sem) for semaphores, but there
is a fundamental difference. The semaphore has an value associated with it. By
calling wait(sem), you automatically blocks or “falls through” depending on the
semaphore value and signal(sem) always increments the value in addition to
releasing a thread/process from the wait queue. the Java wait(), on the other hand,
unconditionally blocks when called. There is no value which automatically gets
tested to determine if you block or not as in a semaphore. The calling thread has the
responsibility to explicitly determine it it should block by calling wait(). For
example the producer makes this determination by explicitly testing if the count is
equal to the buffer size. In addition to release a thread from a Java wait queue, the
consumer must explicitly do something that would release a resource that the
producer is waiting for before calling notify(). This is similar to the signaling
mechanism in semaphores.
Typically the Java wait() is called after the thread is in the critical section and it
discovers (by explicitly testing) that it must wait for some event. The counting
semaphore wait(sem), on the other hand is always called and may or may not block.
Java does not have a built in semaphore, but a semaphore can be coded in Java using
the Java synchronization techniques to make the wait/signal atomic:
Java Threads
Spring 2002
11/29/01
page 13
/**
* Semaphore.java
*
* A basic counting semaphore using Java synchronization.
*
* @author Greg Gagne, Peter Galvin, Avi Silberschatz
* @version 1.0 - July 15, 1999
* Copyright 2000 by Greg Gagne, Peter Galvin, Avi Silberschatz
* Applied Operating Systems Concepts - John Wiley and Sons, Inc.
*/
public final class Semaphore
{
public Semaphore() {
value = 0;
}
public Semaphore(int v) {
value = v;
}
public synchronized void P() {
while (value <= 0) {
try {
wait();
}
catch (InterruptedException e) { }
}
value --;
}
public synchronized void V() {
++value;
notify();
}
private int value;
}
Solution for the Bounded buffer problem using java semaphores:
See fig. { 7.13, 7.14, 7.15}
Java Threads
Spring 2002
11/29/01
page 14