Download Thread

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

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

Document related concepts
no text concepts found
Transcript
INF2 - COMPUTER NETWORKS
Session 3
Threads and Internet
Luiz Angelo Steffenel
[email protected] / [email protected]
Multitasking and Multithreading
• Multitasking:
• having more than one program (process) working at
what seems to be at the same time (running
concurrently)
• The OS assigns the CPU to the different programs in a
manner to give the impression of concurrency
• There are two types of multitasking
• preemptive and cooperative multitasking
• Multithreading:
• extends the idea of multitasking by allowing individual
programs to have what appears to be multiple tasks
• Each task within the program is called a thread
What is a Thread
• A lightweight process that runs inside a
"system process"
• Advantages:
Light (small memory footprint)
• Allows a better resource usage (I/O, network)
•
• Attention
• For those used to C and other "system" languages,
a thread shares variables and data with other
threads
• In Java, each thread is an independent object
The Thread Class
• The Thread class is part of the java.lang package
• Using an object of this class, the corresponding
thread can be stopped, paused, and resumed
• There are many constructors and methods for this
class, we will look at a few of them:
- Thread( String n) - creates a new Thread with the name n
- Thread( Runnable target) - creates a new Thread object
- Thread( Threadgroup group, Runnable target) - creates a
new Thread object in the specified threadgroup
Useful Methods in the Thread class
static methods:
currentThread( );
sleep( );
yield( );
instance methods:
get/setPriority( );
start( );
run( );
isAlive( );
isInterrupted( );
join( );
get/setName( );
setDaemon( );
How to create Java Thread
• Java has multithreading built into it
• Java provides a Thread class for handling threads
• There are two ways to create Thread objects
- creating objects from subclasses of the Java Thread class
- implementing the Runnable interface for an object
Thread
ThreadSubclass
class ThreadX extends Thread {
public void run( ) {
//logic for the thread
}
}
ThreadX tx = new ThreadX( );
tx.start( );
Example
class ThreadCounter extends Thread {
int no_fin;
ThreadCounter (int fin) {
no_fin = fin;
}
public void run () {
for (int i=1; i<=no_fin ; i++){
System.out.println(this.getName()+":"+i);
}
}
}
public static void main (String args[]) {
ThreadCounter cp1 = new ThreadCounter (100);
ThreadCounter cp2 = new ThreadCounter (50);
cp1.start();
cp2.start();
}
How to create Java Thread
- implementing the Runnable interface for an object
Runnable
implements
SomeSubclass
class RunnableY implements Runnable {
public void run ( ) {
//logic for the thread
}
}
RunnableY ry = new RunnableY();
Thread ty = new Thread(ry);
ty.start();
In both methods, the run( ) method should be implemented
Example
class CompteurRunnable implements Runnable {
int id, no_fin, attente;
CompteurRunnable(int id, int fin, int att) {
no_fin = fin;
attente = att;
this.id = id;
}
public void run() {
for (int i = 1; i <= no_fin; i++) {
System.out.println(id + " : " +i);
try {
Thread.sleep(attente);
} catch (InterruptedException e) {};
}
}
public static void main(String args[]) {
Runnable cp1 = new CompteurRunnable(1,100,100);
Runnable cp2 = new CompteurRunnable(2,100,100);
Thread t1 = new Thread(cp1);
Thread t2 = new Thread(cp2);
t1.start();
t2.start();
}
}
Which method is better?
• Extending the Thread class
• When we want to create simple classes with no
additional heritage
• Autonomous applications
• Implementing Runnable
• When a superclass exists
• Ex: applets
public class MyThreadApplet
extends Applet implements Runnable {}
•
This second method is more general and is the only
only we can use for advanced threading (pools of
threads, etc.)
Run or Start?
• When we write the thread code, we
implement the logic inside the run() method
• When we launch a thread, we must call the
start() method
This is a instruction to execute the run() method in a
separate thread
• If we call run(), it is a sequential call (no thread)
•
Threads Life Cycle
Thread States
• At the instantiation:
- Like any other java object
- Still inactive
- Active state:
- When calling start(), the JVM launches a thread
that executes run()
Thread States
• Sleeping or interrupted state:
• sleep(): the thread is removed from the scheduling
list and sleeps for a given time (ms). Comes back to
the list to be executed when possible
• A thread waiting for a resource can be put in an
interrupted state while the resource is not available
• End:
• if stop() is explicitly called
• when run() finishes its execution
Example with sleep
class ThreadCounter extends Thread {
int no_fin;
Int delay;
ThreadCounter (int fin,int att) {
no_fin = fin;
attente=att;
}
public void run () {
for (int i=1; i<=no_fin ; i++) {
System.out.println(this.getName()+":"+i);
try {
sleep(delay);
}
catch(InterruptedException e) {};
}
}
public static void main (String args[]) {
ThreadCounter cp1 = new ThreadCounter (100,100);
ThreadCounter cp2 = new ThreadCounter (50,200);
cp1.start();
cp2.start();
} }
And what about the networks?
• A ServerSocket (or even a DatagramSocket
server) may be unable to answer all clients in
a proper time
• Instead of handling a client at time, threads
can be used to deal with the clients, leaving
the server "free" for new connections
We delegate the work to the threads
while (true) {
accept a connection;
Launch a thread for the socket;
}
•
Threads and Networks:
work delegation
A multithreaded network server
class NetworkService {
private final ServerSocket serverSocket;
private int port = 5000;
public static void main (String[] args) {
serverSocket = new ServerSocket(port);
Thread P1;
try {
while (true) {
P1 = new Thread(new ThServ(serverSocket.accept()));
P1.start();
}
} catch (InterruptedException ex) {
// handle exception;
}
}
}
class ThServ implements Runnable {
private final Socket socket;
ThServ(Socket socket) { this.socket = socket; }
public void run() {
// read and service request on socket
}
}
19
Activity 1
• Implement a basic web server
• The server waits for an HTTP "GET" command
• If the parameter is "/", responds with the content of a file
named "index.html" (on your base directory)
• If the parameter is a filename, answer with the content of
the file
• HTTP/1.1 200 OK \nContent-Type: text/html; charset=utf-8\n\n
+ content
• If the file doesn't exist, answer with "404 not found"
• HTTP/1.1 404 ERROR\n\n
Activity 2
• Develop a MOM (Message Oriented
Middleware) for a computing service
Works like a mail box
• Each client C can send a message with a simple
"problem" to solve (like 1+1) and then closes the
connection
•
• It received a Request ID
The system reads requests in order and compute
the results. An answer is stored under the request ID
• The client may check later if there is an answer
using the request ID
•
A messy output
• The execution/release order depends on the
way the JVM alternates between the threads
• Without any synchronization, it is impossible to
ensure a "thread-safe" result
•
Ex.: some computing operation where the order
matters
• A calculator code (+, -, *, /)
•
"Longer" operations that must be atomic
• Remove money from one account and transfer to another
22
Losing money
abstract class Account {
private int balance = 0;
abstract public void deposit(int amount);
public int getBalance() {
return balance;
}
public void setBalance(int newbalance) {
balance=newbalance;
}
}
23
Losing money
public class asyncAccount extends Account {
public void deposit(int amount) {
int oldbalance = this.getBalance();
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
}
this.setBalance(oldbalance + amount);
}
}
24
Losing money
public class Customer extends Thread {
Account account;
public Customer(Account account) {
this.account = account;
}
public void run() {
try {
for (int i = 0; i < 20; i++) {
account.deposit(10);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
25
Losing money
public class Bankrupt {
private final static int NUMCUSTOMER = 10;
public static void main(String args[]) {
Account account = new asyncAccount();
Customer customer[] = new Customer[NUMCUSTOMER];
for (int i = 0; i < NUMCUSTOMER; i++) {
customer[i] = new Customer(account);
customer[i].start();
}
for (int i = 0; i < NUMCUSTOMER; i++) {
try {
customer[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//Display account balance
System.out.println(account.getBalance());
}
}
Synchronization in Threads
• Synchronization is a mechanism to control the the
execution of different threads so that:
-
when multiple threads access a shared variable, proper
execution can be assured
• Java has the synchronized keyword
•this can be used to identify a segment of code
or method that should be accessible to just a
single thread at a time
• Before entering a synchronization region, a thread
should obtain a lock associated with that region
•if it is already taken, then the thread blocks
(waits) until the lock is released
27
Keeping your money
public class syncAccount extends Account {
public synchronized void deposit(int amount) {
int oldbalance = this.getBalance();
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
}
this.setBalance(oldbalance + amount);
}
}
Synchronization in Threads
• In Java, any object with one or more synchronized
methods is a monitor
• When threads call a synchronized method, only one
thread is let in at a time, the others wait in a queue
• In producer-consumer type applications, consumer
threads might find that there is not enough
elements to consume
• Instead of keep trying, it may decide to wait
• It is the job of the monitor to ensure that the threads
that are waiting for the producer are notified once
the elements are produced
Thread Preemption
• A thread can temporarily release a lock so other
threads can have an opportunity to execute a
synchronized method
• The Object class defined three methods that allow
threads to communicate with each other
- void wait( ) - causes the thread to wait until notified
- void wait(long msec) throws InterruptedException
- void wait(long msec, int nsec) throws
InterruptedException
- void notify( ) - notifies a randomly selected thread waiting
for a lock on this object
- void notifyall( ) - notifies all threads waiting for a lock on this
object
Thread Preemption example
class Producer extends Thread {
Queue queue;
Producer (Queue queue) {
this.queue = queue;
}
public void run( ) {
int i = 0;
while(true) {
queue.add(i++);
}
}
}
Thread Preemption example
class Consumer extends Thread {
String str;
Queue queue;
Consumer (String str, Queue queue) {
this.str = str;
this.queue = queue;
}
public void run( ) {
while(true) {
System.out.println(str + ":"
);
}
}
}
+ queue.remove( )
Thread Preemption example
class Queue {
private final static int SIZE = 10;
int array[ ] = new int[SIZE];
int r = 0;
int w = 0;
int count = 0;
synchronized void add(int i) {
//wait while the queue is full
while (count == SIZE) {
try {
wait( );
}
catch (InterruptedException ie) {
ie.printStackTrace( );
System.exit(0);
}} /* add */
Thread Preemption example
//Add data to array and adjust write pointer
array[w++] = i;
if (w >= SIZE)
w = 0;
//Increment count
++count;
//Notify waiting threads
notifyAll( );
}
synchronized int remove( ) {
//wait while the queue is empty
while (count == 0) {
try { wait( ); }
catch (InterruptedException ie) {
ie.printStackTrace( );
System.exit(0);}}
Thread Preemption example
//read data from array and adjust read pointer
int element = array[r++];
if (r >= SIZE)
r = 0;
//Decrement count
--count;
//Notify waiting threads
notifyAll( ); return element;
}}
public class ProducerConsumer {
public static void main(String args[ ]) {
Queue queue = new Queue( );
new Producer(queue).start( );
new Consumer("ConsumerA", queue).start( );
new Consumer("ConsumerB", queue).start( );
new Consumer("ConsumerC", queue).start( );}}
Scheduling : preemptive vs.
nonpreemptive
• Thread scheduling is the mechanism used to
determine how runnable threads are
allocated CPU time
• A thread-scheduling mechanism is either
preemptive or nonpreemptive
Preemptive scheduling – the thread scheduler
preempts (pauses) a running thread to allow
different threads to execute
• Nonpreemptive scheduling – the scheduler never
interrupts a running thread
•
• The nonpreemptive scheduler relies on the running thread
to yield control of the CPU so that other threads may
execute
Starvation
• A thread that never "quits" a critical zone
may cause starvation (runnable threads,
ready to be executed, wait to be executed
in the CPU a lot of time, maybe even forever)
•
Ex: once entering in the synchronized method, the
thread never leaves it
• Sometimes, starvation is also called livelock
Deadlock
• Deadlock is an error that can be
encountered in multithreads
• It occurs when two or more threads wait
indefinitely for each other to relinquish locks
• Thread-1 holds a lock on object-1 and waits
for a lock on object-2
• Thread-2 holds a lock on object-2 and waits
for a lock on object-1
• The classical Philosophers' Dinner problem
Other ways to control threads
• The synchronized keyword may be a bit
confusing because it can be used in a
method or in a code block
•
In both cases, we need to be "securing" the same
object
• Recent java versions added the support to
explicit Lock objects or Conditions
Lock
import java.util.concurrent.locks.*;
public class SequenceLock extends Thread{
private int[] tab;
private int num;
private Lock theLock;
public SequenceLock(int[] t, int i, Lock theLock){
tab = t;
num = i;
this.theLock = theLock;
}
public void run(){
theLock.lock();
try {
for(int i=0;i<tab.length;i++){
System.out.println("Thread " + num + " tab[" + i + "]=" + tab[i]);
long tps = (long) Math.random() * 100;
try{
this.sleep(tps);
}catch(Exception e){
System.out.println("Erreur " + e);
}
}
} finally {
theLock.unlock();
}
}
}
import java.utils.concurrent.locks.*;
public class LockLauncher {
public static void main(String[] args){
int[] x = new int[10];
Lock myLock = new ReentrantLock();
for(int i=0;i<x.length;i++){
x[i] = i;
}
Thread t1 = new SequenceLock(x,0,myLock);
Thread t2 = new SequenceLock(x,1,myLock);
Thread t3 = new SequenceLock(x,2,myLock);
t1.start();
t2.start();
t3.start();
}
}
Conditions
• When a thread enters a "lock" block, it
doesn't allow anyone else to acquire the lock
What if it is waiting for a resource that another
thread shall provide?
• Ex.: The "bank" transfer code
•
• We need to verify that the account has enough money
• Where shall we test this condition without risking a "bad
preemption" ?
public class Bank
{
public void transfer (int from, int to, int amount)
{
bankLock.lock();
try {
accounts[from] -= amount;
System.out.printf("%10.2f from %d to %d",amount, from, to);
accounts[to] += amount;
} finally {
bankLock.unlock();
}
}
...
private Lock bankLock = new ReentrantLock();
}
Using Conditions
• We cannot test outside the method transfer()
because there is no guarantee that the funds
will be there when the thread enters the
method
• We cannot "stuck" inside the transfer()
method waiting for a refunding as it prevents
other threads to enter it
• Solution: we need to "release" the lock on a
condition
public class Bank
{
public void transfer (int from, int to, int amount)
{
bankLock.lock();
try {
while (accounts[from] < amount)
sufficientFunds.await();
accounts[from] -= amount;
System.out.printf("%10.2f from %d to %d",amount, from, to);
accounts[to] += amount;
sufficientFunds.signalAll();
} finally {
bankLock.unlock();
}
}
...
private Lock bankLock = new ReentrantLock();
private Condition sufficientFunds;
sufficientFunds = bankLock.newCondition();
}
46
Activity 3
• Modify the "SpeedDating" Application
• The speed dating app puts two "sockets" in
contact
• Data is copied thanks to XFerThread
•
You must modify the code so that
• New connections are attached to a "peer list"
• Each X seconds the server shuffles the list and puts two
connections in contact