Survey
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project
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