Networking and
Multi-threading
COMP2396 Object-Oriented Programming and Java
Dr. Kenneth Wong
Connecting to Another Program
Owen would like to extend his fantasy adventure game to a
multi-player online game
He adopted the client-server model where each player will
launch a client on his own machine that will be connected to
a server over the network
A client can then communicate with other clients by sending
and receiving messages to and from the server
In Java, sending and receiving data over the network is just
I/O with a slightly different connection stream
All the low-level networking details are taken care of by
classes in the java.net package
1
Client-Server Model
How it works:
Server, I’d like to
1 A client connects to the server connect to the game Waiting for client
requests
Client A
Server
2 The server makes a connection Ok, you’re in Participants:
and adds the client to the list of 1. Client A
participants
Client A
Server
Server, I’d like to
connect to the game Participants:
3 Another client connects
1. Client A
Ok, you’re in 2. Client B
Client B
Server
2
Client-Server Model
How it works:
“Magician A casts a
4 Client A sends a message to the healing spell” Message
game server received
Client A
Server
“Magician A casts a
healing spell” Message
5 The server distributes the
distributed to all
message to ALL participants
participants
(including the original sender)
Client A
Server
Client B
3
Connecting, Sending, and Receiving
In order to get his game client working, Owen needs to
learn
1. How to establish the initial connection between the client
and server
2. How to send messages to the server
3. How to receive messages from the server
4. How to send outgoing messages to and simultaneously
receive incoming messages from other participants via
the server
Multi-threading
is the solution!
4
Socket Connection
Client and server applications communicate over a Socket
connection
A Socket (java.net.Socket class) is an object that
represents a connection between 2 applications which may
(or may not) be running on 2 different physical machines
To create a Socket connection, a client must know the IP
address and TCP port number of the server application
Socket socket = new Socket("192.168.1.103", 5000);
IP address for the server TCP port number
5
TCP Port
A TCP port is a 16-bit unsigned number assigned to a specific
server application
TCP port numbers allow different clients to connect to the same
machine but communicate with different applications running on
that machine
TCP port numbers from 0 to 1023 are reserved for well known
services (e.g., 80 for HTTP, 23 for Telnet, 20 for FTP, 25 for SMTP,
and 110 for POP3 mail server, etc.)
When writing a server program, you might use any TCP port
number between 1024 to 65535
Only 1 program can be running on a single TCP port. If you try to
bind a program to a port that is already in use, you will get a
java.net.BindException
6
Reading Data from a Socket
The 4 simple steps in reading data from a socket
1. Make a Socket connection to the server
Socket sock = new Socket("127.0.0.1", 5000); 127.0.0.1 is the IP address
for “localhost” (i.e., the one
2. Make an InputStreamReader this code is running on)
InputStreamReader streamReader = new InputStreamReader(sock.getInputStream());
3. Make a BufferedReader
BufferedReader reader = new BufferedReader(streamReader);
4. Read data
String line = reader.readLine();
destination source
buffered characters converted to characters bytes from server
buffered Data on the
characters 011010011
characters chained to chained to server
Client BufferedReader InputStreamReader Socket’s input stream 7
Server
Writing Data to a Socket
The 3 simple steps in writing data to a socket
1. Make a Socket connection to the server
Socket sock = new Socket("127.0.0.1", 5000);
2. Make a PrintWriter
PrintWriter writer = new PrintWriter(sock.getOutputStream());
3. Write data println() adds a new line at
writer.println("message to send"); the end of what it sends
writer.print("another message");
print() doesn’t adds the new line
source destination
characters bytes to server
Server
"message..." 011010011
chained to program
Client PrintWriter Socket’s output stream 8
Server
Example: Daily Advice Client
Example
import java.io.*;
import java.net.*;
public class DailyAdviceClient {
Socket sock;
I/O operations can throw exceptions
public void go() {
try { Make a Socket connection
sock = new Socket("127.0.0.1", 5000);
InputStreamReader streamReader =
new InputStreamReader(sock.getInputStream());
BufferedReader reader = new BufferedReader(streamReader);
String advice = reader.readLine();
System.out.println("Today's advice: " + advice);
reader.close();
} catch (Exception ex) { ex.printStackTrace(); }
}
// code for main()... 9
}
Writing a Simple Server
How it works: ServerSocket
1 A server application creates a 5000
ServerSocket on a specific port
ServerSocket serverSock = new ServerSocket(5000); Server
2 The server application waits for a new 5000
client
Socket sock = serverSock.accept(); Server
3 A client makes a Socket connection to 5000
the server application
Socket
Socket sock = new Socket("192.168.1.103", 5000); Server
4 The server creates a new Socket to 5000
communicate with this client 5000
Socket sock = serverSock.accept(); Server
Socket 10
Writing a Simple Server
The accept() method of a ServerSocket blocks (just sits
there) while it is waiting for a client Socket connection
When a client finally tries to connect, the method returns a
plain Socket on the same port
This Socket knows how to communicate with the client (i.e.,
it knows the client’s IP address and TCP port number)
11
Example: Daily Advice Server
Example
import java.io.*;
import java.net.*;
public class DailyAdviceServer {
String[] adviceList = {"Practice makes perfect", "Never give up",
"Focus on the task at hand", "Don't look back", "Be yourself",
"Believe in your own work"};
ServerSocket serverSock;
public String getAdvice() {
int random = (int) (Math.random() * adviceList.length);
return adviceList[random];
}
public static void main(String[] args) {
DailyAdviceServer server = new DailyAdviceServer();
server.go();
} 12
Example: Daily Advice Server
Example
public void go() {
try {
serverSock = new ServerSocket(5000);
while (true) {
Socket sock = serverSock.accept();
PrintWriter writer = new PrintWriter(sock.getOutputStream());
String advice = getAdvice();
writer.println(advice);
This server application listens for
writer.close();
client requests on port 5000 on the
System.out.println(advice);
machine this code is running on
}
} catch (Exception ex) {
ex.printStackTrace();
}
} // close go
}
The server goes into an infinite loop,
13
waiting for and serving client requests
Multi-threading
The daily advice server code in the previous example has a
serious limitation that it can only handle 1 client at a time
It cannot accept a request from another client until it has
finished with the current client and started the next iteration
of the infinite loop
In order to make the server capable of handling multiple
clients concurrently, separate threads are needed and each
new client Socket should be assigned to a new thread
Similarly, if a client wants to send and receive messages to
and from the server simultaneously, a separate thread
should be created for receiving messages from the server
14
Multi-threading
A thread can be considered as a line of execution. It has its
own call stack for storing method invocations and local
variables
Every application has at least 1 thread running when it is
started (i.e., the main thread)
The main thread can created additional threads (i.e., worker
threads) to handle different tasks in parallel (e.g., reading
from a file, printing to a printer)
Multi-threading refers to the ‘concurrent’ execution of
multiple threads in a single application
Multi-threading often makes an application more responsive
by moving long-running tasks to worker threads
15
Multi-threading in Java
Java has multi-threading built right into the fabric of the
language
A Thread (java.lang.Thread class) is an object that
represents a thread of execution
The 3 simple steps in launching a new thread
1. Make a Runnable object (the thread’s job)
Runnable threadJob = new MyRunnable();
2. Make a Thread object and give it a Runnable (the job)
Thread myThread = new Thread(threadJob);
3. Start the Thread
myThread.start();
16
Runnable Interface
A Runnable object is to a Thread object what a job is to a
worker. It is an instance of a class that implements the
Runnable interface
The Runnable interface defines only 1 method
public interface Runnable {
void run();
}
By passing a Runnable object to the Thread constructor, it
tells the new Thread object which job to run
When the Thread object’s start() method is called, a new
thread of execution starts and the Runnable object’s run()
method is put on the bottom of the new thread’s stack
17
A Multi-threading Example
Example
public class MyRunnable implements Runnable {
public void run() { go(); }
public void go() { doMore(); } What will be the output?
public void doMore() { A top of the stack
System.out.println("top of the stack"); back in main
}
public static void main(String[] args) { B back in main
Runnable threadJob = new MyRunnable(); top of the stack
Thread myThread = new Thread(threadJob);
myThread.start();
System.out.println("back in main");
} doMore()
} myThread.start() go()
main() run()
main thread new thread
18
States of a Thread
When the thread is selected
t.start(); by the JVM thread
When the Thread object’s scheduler to run, its call
start() method is called, it stack becomes active and
moves into the runnable the method on the top of
state and a new call stack the stack is executing
is created
selected to run by the JVM
thread scheduler
RUNNABLE
RUNNING
NEW sent back to runnable by the
JVM thread scheduler
Thread t = new Thread(r);
When a Thread object is
created, it is in the new BLOCKED
state and there is no thread When a thread is sleeping, waiting for
of execution yet another thread to finish, waiting for data
to become available on the stream, etc.,
the JVM thread scheduler will move it to
the blocked state 19
Thread Scheduler
The thread scheduler makes all the decisions about
Which thread moves from runnable to running state
When and under what circumstances a thread leaves the
running state
Where a thread goes when it is kicked out of the running state
There is no API for calling methods on the scheduler (i.e., no
way to control the scheduler)
There are no guarantees about scheduling!
Never base your program’s correctness on the scheduler
working in a particular way!
20
A Multi-threading Example
Scenario 1:
main()starts the The scheduler The scheduler lets The new thread
new thread sends the main the new thread run goes away because
thread out of to completion, its run() completes.
running and back to printing out The main thread
runnable, so that “top of the stack” once again
the new thread can becoming the
run doMore() running thread, and
prints
myThread.start() myThread.start() go() “back in main”
main() main() run() main()
main thread main thread new thread main thread
time
Sample output
top of the stack
back in main
21
A Multi-threading Example
Scenario 2:
main()starts the The scheduler The scheduler lets The scheduler The scheduler The new thread
new thread sends the main the new thread run sends the new selects the main returns to the
thread out of for a little while, not thread back to thread to be the running state and
running and back to long enough for the runnable running thread prints out
runnable, so that run() method to again. main()prints “top of the stack”
the new thread can complete out
run “back in main” doMore()
myThread.start() myThread.start() go() go() go()
main() main() run() run() main() run()
main thread main thread new thread new thread main thread new thread
time
Sample output
back in main
top of the stack
22
Putting a Thread to Sleep
One of the best ways to help your threads take turns is to put them
to sleep periodically
Thread.sleep() can
try { throw an exception
Thread.sleep(2000);
} catch (Exception ex) { ex.printStackTrace(); }
Putting a currently-running thread to sleep will force it to leave the
running state, thus giving another thread a chance to run
A sleeping thread will not wake up before the specified duration (in
milliseconds)
When a thread wake up, it always goes back to the runnable state
23
Concurrency Problem
Concurrency problem may occur when 2 or more threads
have access to the same object on the heap
Having 2 or more threads accessing the same object at
approximately the same time will result in a race condition,
and may cause data corruption
Example
Mr. and Mrs. Smith share a bank account
They always check the balance before making a withdrawal to
ensure their account will not be overdrawn
The problem is they always fall asleep in between checking the
balance and making the withdrawal
When they wake up, they make the withdrawal without
checking the balance again
24
Mr. & Mrs. Smith Example
Example
public class BankAccount {
private int balance = 100;
public int getBalance() { return balance; }
public void withdraw(int amount) {
balance = balance – amount;
}
}
One single shared
public class SmithJob implements Runnable { bank account
private BankAccount account = new BankAccount();
public static void main(String[] args) {
SmithJob theJob = new SmithJob(); 2 threads having the
Thread mrSmith = new Thread(theJob); same job that accesses
Thread mrsSmith = new Thread(theJob); the same bank account
mrSmith.setName("Mr. Smith");
mrsSmith.setName("Mrs. Smith");
25
Mr. & Mrs. Smith Example
Example
mrSmith.start();
mrsSmith.start();
} Making a withdrawal
of $60 twice
public void run() {
for (int x = 0; x < 2; x++) {
makeWithdrawal(60);
}
}
Checking the balance
private void makeWithdrawal(int amount) { before withdrawal
if (account.getBalance() >= amount) {
System.out.println(getName() + " is about to withdraw");
try {
System.out.println(getName() + " is going to sleep");
Thread.sleep(500); Falling asleep
} catch (Exception ex) { ex.printStackTrace(); }
26
Mr. & Mrs. Smith Example
Example Waking up and
completing the
System.out.println(getName() + " wakes up"); withdrawal
account.withdraw(amount);
System.out.println(getName() + " completes the withdrawal");
if (account.getBalance() < 0) {
System.out.println("Overdrawn!"); Checking for overdrawn
}
}
else {
System.out.println("Not enough money for " + getName());
}
}
private String getName() {
return Thread.currentThread().getName();
}
}
27
Mr. & Mrs. Smith Example
Sample output
Mr. Smith is about to withdraw
Mr. Smith is going to sleep
Mrs. Smith is about to withdraw
Mrs. Smith is going to sleep
Mr. Smith wakes up
Mrs. Smith wakes up
Mr. Smith completes the withdrawal
Mrs. Smith completes the withdrawal
Overdrawn!
Overdrawn!
Not enough money for Mr. Smith
Not enough money for Mrs. Smith
28
Synchronization
To solve the concurrency problem in the previous example, we
need to make sure that once a thread has checked the account
balance, it has a guarantee that it can wake up and finish the
withdrawal before any other thread can check the account balance
This can be achieved by making the makeWithdrawal() method
atomic
Use the keyword synchronized to modify a method so that only 1
thread at a time can access it
private synchronized void makeWithdrawal(int amount) {
// ...
}
To protect your data (like the bank account), synchronize the
methods that act on that data
29
Using an Object’s Lock
Every Java object has a lock with a single key
Most of time, the lock is unlocked
Object locks come into play only when
there are synchronized methods
A thread can enter one of the synchronized
methods only if it can get hold of the object’s key
Even if an object has more than 1 synchronized method, there is
still only 1 key
Once a thread has entered a synchronized method on an object,
no other threads can enter any synchronized methods on the
same object
30
Deadlock Problem
A thread deadlock happens when you have two
threads, both of which are holding a key the other
thread wants
1 Thread A enters a synchronized 3 Thread B enters a synchronized 5 Thread A wakes up (still holding
method on object foo, and gets method on object bar, and gets the foo key) and tries to enter a
the key. the key. synchronized method on object
bar, but can’t get that key
because B has it. A goes to the
waiting lounge, and waits until
the bar key becomes available.
2 Thread A goes to sleep, holding
the foo key. 4 Thread B tries to enter a
synchronized method on object
foo, but can’t get that key
because A has it. B goes to the
waiting lounge, and waits until 6 Thread A can’t run until it can
the foo key becomes available. get the bar key, but B is holding
B keeps the bar key. the bar key, and B can’t run
until it gets the foo key that A is
holding and …
31
Deadlock Problem
There is no way out of this scenario and the two
threads will simply sit and wait forever!
Java has no mechanism to handle deadlock, and it
won’t even know a deadlock occurred
It is up to you to design carefully!
32