Building a Robust Multithreaded Server in Java Alexander Day Chaffee jGuru Training by the MageLang Institute alex@jguru.com Copyright © 1998 Alex Chaffee Abstract • Mark Andreesen says, ”Client-side Java is dead." Fortunately, server-side Java is alive and kicking! This session will quickly introduce you to the concepts of threading and networking in Java. We then build a robust, multithreaded server (using a simple chat as our application). We examine several different threading models, specifically comparing the one-thread-per-connection model with the queuing model. Copyright © 1998 Alex Chaffee Introduction • jGuru Training by the Magelang Institute – http://www.jguru.com – Java Training and Consulting • Alex Chaffee – Creator of Gamelan – Cool Java Dude Copyright © 1998 Alex Chaffee Background • Java language basics • Java networking • Java threading Copyright © 1998 Alex Chaffee Overview • • • • • Start slow, end up really really fast Part I: Networking and I/O in Java Part II: Threads and concurrency Part III: Multithreaded server design Part IV: Implementations – Design decisions – Object models – Problems and solutions Copyright © 1998 Alex Chaffee Part I: Networking and I/O in Java Copyright © 1998 Alex Chaffee Java and Networking • • • • • Built into language One of the 11 buzzwords Network classloader Java.Net API Based on TCP/IP, the internet protocol Copyright © 1998 Alex Chaffee Client-server • Difference between client and server is semantic • It’s all just peers talking to each other • Protocol - roles, vocabulary, rules for communication Copyright © 1998 Alex Chaffee TCP/IP: The Internet Protocol Application Layer (HTTP, FTP, SMTP) Transport Layer (TCP, UDP) Internet Layer (IP) Physical Network Copyright © 1998 Alex Chaffee Sockets and Ports • Port: a meeting place on a host – One service per port – 1-1023 = well-known services – 1024+ = experimental services, temporary • Socket: a two-way connection Copyright © 1998 Alex Chaffee Sockets and Ports (Diagram) Client Socket port 13 Time Service port 80 Web Service Socket Server Copyright © 1998 Alex Chaffee The Socket Class • • • • Socket(String host, int port) InputStream getInputStream() OutputStream getOutputStream() void close() Copyright © 1997 Alex Chaffee Socket Code Socket s = new Socket(“www.sigs.com”, 80); • Simple, huh? • Creates the connection • We still need to get the data there and back again Copyright © 1997 Alex Chaffee Streams • A stream is a sequence of bytes • I/O from disk, network, memory, etc. is handled in exactly the same way Copyright © 1997 Alex Chaffee InputStream and OutputStream • InputStream: abstract int read() • OutputStream: abstract void write(int b) • Common: abstract void close() Copyright © 1997 Alex Chaffee Filter Streams • Like photographic filters • Change the input (or output) on the way through • E.g.: BufferedInputStream, PrintStream, LineNumberInputStream Copyright © 1997 Alex Chaffee Filter Streams (Diagram) Abc Abc FileInputStream AllCapsInputStream ABC BC DataInputStream NoVowelInputStream readLine() “BC” DataInputStream in = new DataInputStream( new NoVowelInputStream( new AllCapsInputStream( new FileInputStream(“input.txt”)))); String line = in.readLine(); Copyright © 1997 Alex Chaffee PrintStream • print() – outputs an object, primitive, or String • println() – same, but adds a newline • System.out is a PrintStream PrintStream ps = new PrintStream(outputstream) Copyright © 1997 Alex Chaffee DataInputStream • int readInt() • type readType() • String readLine() DataInputStream in = new DataInputStream(inputstream) Copyright © 1997 Alex Chaffee Browser.java (source) import java.net.*; import java.io.*; public class Browser { public static void main(String[] args) { String host = "www.stinky.com"; int port = 80; String file = "/index.html"; Copyright © 1997 Alex Chaffee Browser.java (source) Socket s = new Socket(host, port); InputStream strIn = s.getInputStream(); OutputStream strOut = s.getOutputStream(); PrintStream out = new PrintStream(strOut); DataInputStream in = new DataInputStream(strIn); Copyright © 1997 Alex Chaffee Browser.java (source) out.println("GET " + file + " HTTP/1.0"); out.println(); String line; while ((line = in.readLine()) != null) { System.out.println(line); } in.close(); out.close(); Copyright © 1997 Alex Chaffee Java Servers • A server listens on a port and accepts connections • Must be applications (or signed applets) • Only one service per port for the entire host machine – Java throws an exception if you try to open more than one Copyright © 1998 Alex Chaffee ServerSocket ServerSocket ss = new ServerSocket(1234); Socket socket = ss.accept(); • accept() returns only after a client makes the connection Copyright © 1997 Alex Chaffee ServerSocket • Now we have a socket, so we can call... InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); Copyright © 1997 Alex Chaffee EchoServer.java ServerSocket ss = new ServerSocket(1234); Socket s = ss.accept(); InputStream in = s.getInputStream(); OutputStream out = s.getOutputStream(); Copyright © 1997 Alex Chaffee EchoServer.java int ch; while ((ch = in.read()) != -1) { out.write(ch); } out.close(); in.close(); Copyright © 1997 Alex Chaffee EchoServer Demo • • • • Connect Type some stuff Disconnect Problem: Multiple simultaneous connections – Connections are made, but server doesn’t respond – Even if we loop, can still handle only one at a time Copyright © 1998 Alex Chaffee Multiple Connections • Solution: – Java threads! Copyright © 1998 Alex Chaffee Part II: Threads Copyright © 1998 Alex Chaffee Java Threads • A thread is not an object • A thread is a flow of control • A thread is a series of executed statements • A thread is a nested sequence of method calls Copyright © 1998 Alex Chaffee The Thread Object • A thread is not an object • A Thread is an object void start() – Creates a new thread and makes it runnable void run() – The new thread begins its life inside this method Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Thread Creation Diagram Object A Object BThread (extends Thread) BThread() { } Thread t = new BThread(); t.start(); doMoreStuff(); void start() { // create thread } void run() { doSomething(); } Copyright © 1998 Alex Chaffee Runnable Interface • A helper to the thread object • The Thread object’s run() method calls the Runnable object’s run() method • Allows threads to run inside any object, regardless of inheritance Copyright © 1998 Alex Chaffee Runnable Example Talker talker = new Talker(); Thread t = new Thread(talker); t.start(); --class Talker implements Runnable { public void run() { while (true) { System.out.println(“yakitty yak”); } } } Copyright © 1998 Alex Chaffee Blocking Threads • When reading from a stream, if input is not available, the thread will block • Thread is suspended (“blocked”) until I/O is available • Allows other threads to automatically activate • When I/O available, thread wakes back up again – Becomes “runnable” – Not to be confused with the Runnable interface Copyright © 1998 Alex Chaffee Thread Scheduling • In general, the runnable thread with the highest priority is active (running) • Java is priority-preemptive – If a high-priority thread wakes up, and a low-priority thread is running – Then the high-priority thread gets to run immediately • Allows on-demand processing – Efficient use of CPU Copyright © 1998 Alex Chaffee Thread Starvation • If a high priority thread never blocks • Then all other threads will starve • Must be clever about thread priority Copyright © 1998 Alex Chaffee Thread Priorities: General Strategies • Threads that have more to do should get lower priority • Counterintuitive • Cut to head of line for short tasks • Give your I/O-bound threads high priority – Wake up, immediately process data, go back to waiting for I/O Copyright © 1998 Alex Chaffee Race Conditions • Two threads are simultaneously modifying a single object • Both threads “race” to store their value • In the end, the last one there “wins the race” • (Actually, both lose) Copyright © 1998 Alex Chaffee Race Condition Example class Account { int balance; public void deposit(int val) { int newBal; newBal = balance + val; balance = newBal; } } Looks good, right? Copyright © 1998 Alex Chaffee Race Condition Example class Account { int balance; public void deposit(int val) { int newBal; newBal = balance + val; balance = newBal; } } What if we are swapped out right here? Copyright © 1998 Alex Chaffee Thread Synchronization • Language keyword: synchronized • Takes out a monitor lock on an object – Exclusive lock for that thread • If lock is currently unavailable, thread will block Copyright © 1998 Alex Chaffee Thread Synchronization • Protects access to code, not to data – Make data members private – Synchronize accessor methods • Puts a “force field” around the locked object so no other threads can enter • Actually, it only blocks access to other synchronizing threads • A rogue thread can "sneak in" and modify a variable it has access to Copyright © 1998 Alex Chaffee Synchronization Example class Account { private int balance; synchronized public void deposit(int val) { int newBal; newBal = balance + val; balance = newBal; } synchronized public void withdraw(int val) { int newBal; newBal = balance - val; balance = newBal; } } Copyright © 1998 Alex Chaffee Thread Deadlock • If two threads are competing for more than one lock • Example: bank transfer between two accounts – Thread A has a lock on account 1 and wants to lock account 2 – Thread B has a lock on account 2 and wants to lock account 1 Copyright © 1998 Alex Chaffee Avoiding Deadlock • No universal solution • Ordered lock acquisition • Encapsulation (“forcing directionality”) • Spawn new threads • Check and back off • Timeout • Minimize or remove synchronization Copyright © 1998 Alex Chaffee Wait and Notify • Allows two threads to cooperate • Based on a single shared lock object Copyright © 1998 Alex Chaffee Wait and Notify: Code • Consumer: synchronized (lock) { while (!resourceAvailable()) { lock.wait(); } consumeResource(); } Copyright © 1998 Alex Chaffee Wait and Notify: Code • Producer: produceResource(); synchronized (lock) { lock.notifyAll(); } Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify Sequence Lock Object 1. synchronized(lock){ 2. lock.wait(); 9. consumeResource(); 10. } 3. produceResource() 4. synchronized(lock) 5. lock.notify(); 6.} 7. Reacquire lock 8. Return from wait() Consumer Thread Producer Thread Copyright © 1998 Alex Chaffee Wait/Notify: Details • Often the lock object is the resource itself • Sometimes the lock object is the producer thread itself Copyright © 1998 Alex Chaffee Wait/Notify: Details • Must loop on wait(), in case another thread grabs the resource... – After you are notified – Before you acquire the lock and return from wait() • Use lock.notifyAll() if there may be more than one waiting thread Copyright © 1998 Alex Chaffee Wait/Notify Example: Blocking Queue class BlockingQueue extends Queue { public synchronized Object remove() { while (isEmpty()) { wait(); // really this.wait() } return super.remove(); } public synchronized void add(Object o) { super.add(o); notifyAll(); // this.notifyAll() } } Copyright © 1998 Alex Chaffee Part III: Multithreaded Server Design Copyright © 1998 Alex Chaffee Different Models of Information Flow • • • • Pull model Push model: One thread per client Buffer model: One thread per task Single-thread model Copyright © 1998 Alex Chaffee Pull-based Flow • Consumer asks producer for new information • Demand-driven problems – Examples • Not appropriate for chat/messagepassing server Copyright © 1998 Alex Chaffee Push-based Flow • Producer triggers sequence of events • One thread per client • Receives message, delivers it, waits Copyright © 1998 Alex Chaffee Push-based: Pro • Simple • Efficient use of CPU – Events occur only when message available no polling – No time wasted passing information from one stage to the next Copyright © 1998 Alex Chaffee Push-based: Con • Much thread swapping • Slow receivers can stall upcoming messages from sender Copyright © 1998 Alex Chaffee Buffer Model • Independent threads • Communicate via buffers • Producer puts message in buffer, consumer takes message out Copyright © 1998 Alex Chaffee Buffer Model: Pro • Can separate tasks into separate threads • Optimize and prioritize per task – e.g. Processing existing messages is more important than accepting new connections • Small number of threads means fewer chances for deadlock – Easier to model the system and avoid any chance of deadlock Copyright © 1998 Alex Chaffee Buffer Model: Con • More complex code • Must deal with deadlock and starvation Copyright © 1998 Alex Chaffee The One-Thread Model • All work done by a single thread • Finishes processing one request before going to the next Copyright © 1998 Alex Chaffee The One-Thread Model (Cont.) • Pro: – Suitable for low-traffic scenarios – Quick to implement – Avoids any possibility of deadlock or race conditions • Con – Unsuitable for high-traffic server – Hard to implement things like timers or callbacks Copyright © 1998 Alex Chaffee Part IV: Implementations Copyright © 1998 Alex Chaffee Implementation 1: Push Model Chat Server • • • • • Listens on a port Accepts connections Spawns threads, one per connection Receives a message from one client Dispatches it to all clients Copyright © 1998 Alex Chaffee Chat Server Objects • ChatServer – Main • ClientHandler – One per connection – Threaded • Dispatcher – Vector Copyright © 1998 Alex Chaffee Chat Server Objects (Diagram) ChatServer Dispatcher ClientHandler ClientHandler Socket Copyright © 1998 Alex Chaffee Socket ClientHandler • Stores information for a single client Socket incoming; int id; Dispatcher dispatcher; Copyright © 1997 Alex Chaffee ClientHandler.run() • • • • generate filter streams read a line send it to the dispatcher loop Copyright © 1997 Alex Chaffee ClientHandler.send() • sends a single message to the client • called by the dispatcher • uses the socket Copyright © 1997 Alex Chaffee Dispatcher • Keeps a list of all client handlers • Uses java.util.Vector – synchronized list structure Copyright © 1997 Alex Chaffee Dispatcher.dispatch() • Sends a message to all clients • Accepts a String and an identifying integer • Enumerates down all client handlers and calls each one’s send() method Copyright © 1997 Alex Chaffee ChatServer • • • • • One method: main() Creates a new ServerSocket Creates a new Dispatcher Listens for connections When connection received, creates a new ClientHandler and starts a new thread running inside it • Adds it to the Dispatcher Copyright © 1997 Alex Chaffee Push model: Pro and Con • Pro – Straightforward object, thread models – Efficient CPU usage • Threads unblock only when message available • Con – Must send message to all clients before receive new one – No upper limit on number of threads • May thrash or starve Copyright © 1998 Alex Chaffee Implementation 2: Buffer Model Message Server Copyright © 1998 Alex Chaffee Queuing Model • Producer and consumer communicate via a buffer • Buffer is a queue • First in, first out Copyright © 1998 Alex Chaffee Blocking Queue • If a thread tries to remove an item from an empty queue • Then it blocks until an item is placed inside • Uses wait/notify technique Copyright © 1998 Alex Chaffee JDK 1.2 Collections • • • • Set List Map Queue – Built on top of LinkedList Copyright © 1998 Alex Chaffee Basic Object Model • • • • • Acceptor (thread) Clients (set) Receiver (thread) Incoming (queue) Processor (thread) Copyright © 1998 Alex Chaffee Basic Object Model (Fig.) Acceptor create write Clients Processor read Receiver read write Incoming Thread Data Copyright © 1998 Alex Chaffee Acceptor Thread • Listens for incoming connections • Creates a client object • Adds it to clients set Copyright © 1998 Alex Chaffee Clients Set • Set of all active clients • Synchronized methods – Only one thread at a time can access Copyright © 1998 Alex Chaffee Receiver Thread • Walks down clients set • For each client, checks if message is (fully) available • Reads message and places it on incoming queue • Uses non-blocking input streams Copyright © 1998 Alex Chaffee Receiver Thread: Alternative • Alternative: one receiver thread per client • Each receiver thread blocks until input available • Con: – Reach number of threads bottleneck – Scheduling not determined - could starve or do in weird order Copyright © 1998 Alex Chaffee Incoming Queue • Queue of messages • Synchronized methods – Only one thread at a time can access • Blocking queue – If a thread tries to get a message from an empty queue, it blocks until another thread adds a message Copyright © 1998 Alex Chaffee Processor Thread • While incoming queue is not empty • Pops message off incoming queue • Delivers it Copyright © 1998 Alex Chaffee Full Object Model • Checker (thread) • Problems (list) • Auditor (thread) Copyright © 1998 Alex Chaffee Full Object Model (Fig.) Auditor Problems Checker Processor Clients Acceptor Incoming Receiver Thread Data Copyright © 1998 Alex Chaffee Problems List • List of clients who have problems • Receiver can add a client to problems list if it gets a socket read error • Processor can add a client to problems list if it gets a socket write error • Checker thread periodically disconnects them all Copyright © 1998 Alex Chaffee Checker Thread • Wakes up every N seconds • Walks list of clients • Move to problems list – If N seconds has passed (“timeout”) – If socket has a problem • Walk problems list and actively close each client Copyright © 1998 Alex Chaffee Auditor Thread • Every N seconds, wakes up • Collects and prints statistics Copyright © 1998 Alex Chaffee Non-Blocking Input Stream • Needed for receiver to do its job • If a full message is not available, it does NOT block • Instead, it returns null • Tricky to implement – Must use available(), mark() and reset() cleverly Copyright © 1998 Alex Chaffee Thread Safety Policies • All data objects synchronized • All data objects independent • Therefore no possibility of deadlock in the data objects themselves Copyright © 1998 Alex Chaffee Thread Safety Policies • Thread objects must be very careful to avoid deadlock • Current model: no multi-object transactions • Example – Checker does clients.remove(c) then problems.add(c) – NOT synchronized(clients) { synchronized(problems) { clients.remove(c); problems.add(c); }} – It's OK if it's interrupted between remove and add Copyright © 1998 Alex Chaffee Priority: Why? • In low-volume server, all threads are normally blocked • When something needs to happen, it happens • That's the beauty of threads! • So priorities are irrelevant – If you don’t know what you’re doing, don’t mess with priorities Copyright © 1998 Alex Chaffee Priority: Why? • In high-volume server, many threads always have something to do • Important to do things in the right order • Deal with starvation issue Copyright © 1998 Alex Chaffee Priorities: General Strategies • Threads that have more to do get lower priority • Counterintuitive • Cut to head of line for short tasks Copyright © 1998 Alex Chaffee Priorities: Queuing Strategies • Most important: keep incoming queue empty • So we don't get bogged down • Minimize latency for received messages Copyright © 1998 Alex Chaffee Priorities: Decisions • Don't want to add new messages if we're already busy – Therefore processor > receiver • Don't want to accept new connections if we're already busy – Therefore processor > acceptor • Implication: – Receiver and acceptor may starve? – No: queue will eventually get cleared Copyright © 1998 Alex Chaffee Priorities: Decisions • Want to be able to accept new connections, even if a currently connected client has something to say – Therefore acceptor > receiver Copyright © 1998 Alex Chaffee Priorities: Decisions • Checker's job is brief but important • Needs to wake up, check all clients, and go back to sleep • Therefore checker > processor Copyright © 1998 Alex Chaffee Priorities: Decisions • Auditor's job is brief but important • Need to get accurate statistics -timing is crucial • Can't wait around for other threads to finish • Therefore auditor > checker Copyright © 1998 Alex Chaffee Priorities: Conclusion • • • • • • Auditor - 10 Checker - 8 Processor - 6 Acceptor - 4 Receiver - 2 Goes by twos to accommodate Windows threading model Copyright © 1998 Alex Chaffee Processor Thread Behavior • Processor thread does: 1. Pop message off incoming queue 2. Deliver it 3. Loop • Very independent Copyright © 1998 Alex Chaffee Processor Thread Blocking • • • • Step 1 (pop message) may block If queue is empty This is good Allows other threads to wake up and fill queue Copyright © 1998 Alex Chaffee Processor Thread Blocking • Step 2 (delivery) may block – If network is busy – If OS socket buffer is full • Bad, because queue is still full • Increases latency (delivery time) for already-queued messages – While new messages are arriving, old messages should be delivered instead Copyright © 1998 Alex Chaffee Multiple Processor Threads • Solution: have many processor threads working simultaneously • If one blocks, another picks up the next message Copyright © 1998 Alex Chaffee Multiple Processors (Fig.) Acceptor create write Clients Processor Processor Processor Processor read Receiver read write Incoming Thread Data Copyright © 1998 Alex Chaffee How Many Threads? 1. Constant, tuned per application 2. Dynamic a/k/a thread pooling Copyright © 1998 Alex Chaffee Thread Pooling • Thread pool keeps a certain number of threads alive • Other threads ask the thread pool to perform a task • If an existing thread is available, that thread performs the task • Else a new thread is created, or the task blocks until one is available – (depending on policy) Copyright © 1998 Alex Chaffee Thread Pooling (Cont.) • Removes overhead of creating threads • Allows modular task strategy Copyright © 1998 Alex Chaffee Information Flow • Decoupling threads with buffers is a good idea – Maximizes concurrency – Smooths out bursty differences in rate • Can cause problems with information flow – Producers outpace Consumers – Too many threads Copyright © 1998 Alex Chaffee Limiting Flow • Bounded Buffers – Stalls producers if they outpace consumers • Bounded Thread Pools – Prevents thrashing • Rate-adjustment / back-pressure – Consumer asks Producer to slow down Copyright © 1998 Alex Chaffee Another Problem • One message, multiple recipients, one processor thread • If one recipient is in Finland, it will stall the remaining recipients for that message Copyright © 1998 Alex Chaffee Another Problem: Solutions • Solution 1: one message queue per client; thread pool applied to these delivery queues • Solution 2: split multi-message into several single-recipient messages – Called a Splitter (or Fork or Multicaster) – Also possible: Routers, Mergers, Collectors, Combiners, Conduits -- see Lea’s Concurrent Programming in Java Copyright © 1998 Alex Chaffee Debugging Techniques • Make sure to trap all Throwables in your Thread object’s run() method – Otherwise a stray OutOfMemoryError or NullPointerException will kill your thread and it will look like deadlock • Good logging procedure is vital – I use a global log method that outputs the name of the thread and the time in addition to the message – Name your threads at creation time Copyright © 1998 Alex Chaffee Thread Dump • Control-backslash in Solaris • Control-break in Windows – Unfortunately, it scrolls – There’s no way to redirect standard error – Oops Copyright © 1998 Alex Chaffee Thread Watcher • Data object • All tasks (threads) have a pointer to the master ThreadWatcher • Put debug code in your tasks to inform the ThreadWatcher when they change state • watcher.setIdle(); • Message m = incoming.remove(); • watcher.set(“Processing message “ + m); Copyright © 1998 Alex Chaffee Thread Watcher (cont.) • ThreadWatcher has its own thread – Periodically prints status of threads – Prints an error message if one thread is blocked for too long • Alternative to fancy GUI thread debugger – Symantec Café – Compuware DevPartner – et al. Copyright © 1998 Alex Chaffee Bots • PingBot – periodically sends a message to itself, measures the latency • QuoteBot – sends random Zippy quotes to mimic a chat room Copyright © 1998 Alex Chaffee Measurements • Number of bots before sharp latency increase • Average latency of single PingBot for N ZippyBots Copyright © 1998 Alex Chaffee Test Architecture • Need at least three machines – Server – Zippy Bot Host – Ping Bot Host • So they don’t interfere with one another • Should also have some clients on a far away machine – If your server will be running on the Internet Copyright © 1998 Alex Chaffee Conclusion Copyright © 1998 Alex Chaffee Thanks to • • • • • Eric Malmstrom Carl Muckenhoupt Gerry Seidman Greg Travis For helping prepare this talk Copyright © 1998 Alex Chaffee Where to Get More Information: Network Programming • Cornell & Horstmann, Core Java (Sunsoft press) • Harold, Java Network Programming (O’Reilly) • Hughes et al., Java Network Programming, (Manning, an imprint of Prentice-Hall) Copyright © 1998 Alex Chaffee Where to Get More Information: Multithreading – Cornell & Horstmann, Core Java (sunsoft press) – Oaks and Wong, Java Threads (o’reilly) – Lea, Concurrent Programming in Java (Addison Wesley) – Travis, Using thread pools to increase threading efficiency, developer.com, http://www.Developer.Com/journal/techworkshop/06 0498_thread.html – Travis, Working with the blocking queue, http://www.Developer.Com/journal/techworkshop/09 1098_blockq.html Copyright © 1998 Alex Chaffee Where to Get More Information: • Web sites – http://www.jGuru.com/ (Java Training) – http://www.Developer.com/ (Gamelan) – http://www.Javaworld.com/ (magazine) – http://www.Purpletech.com/ (author’s site) Copyright © 1998 Alex Chaffee