0% found this document useful (0 votes)
3 views

Ch5 - Parallelism and Distributed Objects

The document outlines the concepts of parallelism and distributed objects in the context of Object-Oriented Programming II, focusing on multithreading and its applications. It discusses the differences between multithreading and concurrent process execution, the lifecycle of threads, synchronization mechanisms, and the use of daemon threads. Additionally, it provides examples of multithreading in Java, emphasizing thread management and synchronization techniques.

Uploaded by

anwarouanas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

Ch5 - Parallelism and Distributed Objects

The document outlines the concepts of parallelism and distributed objects in the context of Object-Oriented Programming II, focusing on multithreading and its applications. It discusses the differences between multithreading and concurrent process execution, the lifecycle of threads, synchronization mechanisms, and the use of daemon threads. Additionally, it provides examples of multithreading in Java, emphasizing thread management and synchronization techniques.

Uploaded by

anwarouanas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 64

2nd year of Computer Engineering Diploma

OBJECT ORIENTED PROGRAMMING II

CHAPTER V
PARALLELISM AND DISTRIBUTED OBJECTS
2024/2025 Prof. Abdelhakim HANNOUSSE [email protected]
Outline
2

 Parallelism through Multithreading  Distributed Objects


 Introduction to Parallelism  Introduction to Object Distribution

 Multithreading  Sockets (Low-level Communication)

 Multithreading vs Concurrent Process Execution ◼ Socket Mechanism


 Multithreading in UML ◼ Socket Types
 Multithreading in Java ◼ Sockets in Java

 Thread Lifecycle ◼ TCP mode

 Priorities of threads ◼ UDP mode

 Daemon threads  RMI (High-level Abstraction)

 Thread Synchronization ◼ RMI Mechanism

◼ Synchronization by Monitors ◼ RMI Architecture

◼ Synchronization by Semaphores ◼ RMI in Java


Parallelism through Multithreading
Introduction to Parallelism
4

 Parallelism involves executing multiple tasks at the same time to improve efficiency
and reduce the overall execution time.
 Two main types of parallelisms exist:
 True parallelism: case of multiple processors/cores + dedicated OS.
 Pseudo-parallelism: case where tasks take turns (interleaving).
 Parallelism isn’t just for speed or comfort — it’s essential for making complex programs
feasible within realistic time limits.
 Modern applications (e.g., AI, big data, simulations) involve computations that are too large
or time-sensitive to be handled sequentially.
 Systems like web servers, cloud services, and databases rely on parallelism to serve
thousands/millions of users concurrently.
 Applications like autonomous vehicles and robotics must process multiple streams of data
simultaneously to meet real-time constraints.
Multithreading
5

 Multithreading is based on the concept of having multiple execution parts within the
same program, known as threads, which can run independently.
 A thread is an execution unit that represents an independent execution path inside a
program. A thread runs autonomously and in parallel with other threads.
 Multithreading can be true or pseudo-parallelism depending on hardware and OS:
 On multi-core processors, threads may run simultaneously ➤ True parallelism

 On single-core processors or when there are not enough cores, threads are
interleaved by the CPU scheduler ➤ Pseudo-parallelism
 In every Java application, the JVM starts a main thread that runs the code within the
main method ➤ every Java application has at least one thread.
 Each thread has a priority. Threads with higher priority are executed preferentially
over threads with lower priority.
Multithreading vs. Concurrent Process Execution (1/2)
6

Multithreading Concurrent Process Execution


Facilitate the simultaneous execution of Improve performance by simultaneously
multiple parts of the same program. executing independent programs.
Threads share the same resources but Processes are independent processes and
have their own set of registers. use separate resources.
Programming threads is more complex Minimal need for synchronization
since it requires proper synchronization between tasks.
to avoid conflicts.
Communication is easier through shared Communication needs proper mechanisms.
memory.
Crash in one thread may crash the whole A process crash does not affect others.
process.
Multithreading vs. Concurrent Process Execution (2/2)
7

Multithreading Concurrent Process Execution


Thread creation is faster (up to 100x) Process creation is slower and more
resource-heavy
Better on single-processor systems — Less efficient on single-core CPUs —
Context switching cost is lighter than Context switching cost is heavier due to
processes but still consumes resources. full memory/context separation.
Usefulness of Multithreading
8

 Applications performing intensive calculations can benefit from multithreading by


dividing the computations across multiple threads ➤ An image processing program
can use separate threads to process different parts of an image simultaneously,
thereby improving processing speed.
 It allows maintaining the responsiveness of the user interface while performing
intensive operations in the background ➤ A web browser can use a separate thread
to load content while allowing the user to interact with the interface.
 Server applications can handle multiple requests simultaneously by using separate
threads for each client ➤ An email server can manage multiple client connections in
parallel.
Multithreading in UML
9
class MyThread extends Thread {
@Override
Multithreading in Java public void run() {
//Code to be executed independently
}
10 }
 In Java, thread management is facilitated class ThreadTest {
by the Thread class, which is part of the public static void main(String[] args) {
java.lang package. There are two main MyThread thread = new MyThread();
thread.start();
ways to create a thread: }
 Extend the Thread class and override }

the run() method. When the start()


class MyThread implements Runnable {
method is called, a thread is created @Override
and starts executing the run() method of public void run() {
//Code to be executed independently
the object. }
 Implement the Runnable interface, which }
forces the definition of the run() method. class ThreadTest {
Then, you simply create a Thread with public static void main(String[] args) {
Thread thread = new Thread(new MyThread());
one of the constructors that receives a thread.start();
Runnable. Finally, call start() on the }
Thread. }
Thread Lifecycle
11

new myThread() Resource


obtained Attempt to access a
guarded resource

myThread.start() notify(),
notifyAll()

myThread.wait()

timeout
elapsed run()
returns

myThread.sleep(timeout)
myThread.wait(timeout)
myThread.join(timeout)
Methods of the Thread class
12

Method Description
currentThread() Returns the thread currently being executed.
getName()/setName() Returns/Updates the thread’s name. Default names form: Thread-N
isAlive() Returns true if the thread is not in the NEW or TERMINATED state.
start() Transitions a thread from NEW to RUNNABLE state.
run() Defines the code that the thread executes. It is invoked automatically
when the thread reaches the RUNNING state after start() is called.
sleep(n) Stops the execution of a thread for n milliseconds.
join() Waits for the end of the thread to proceed to the next statement.
getState() Returns the state of the thread (NEW, RUNNABLE, BLOCKED, WAITING,
TIMED_WAITING, TERMINATED).
getPriority()/setPriority(int) Returns/Updates the thread's priority.
setDaemon(boolean) Marks the thread as a daemon thread or a regular or user thread.
Priorities of Threads
13

 Thread priority is a mechanism used by the thread scheduler to determine which


thread to execute with priority.
 In Java, each thread is assigned a numerical priority, typically between 1 and 10,
where 1 is the minimum priority and 10 is the maximum priority. By default, each
thread inherits the priority of its parent thread.
 Priorities are defined by static constants in the Thread class. The minimum and
maximum priorities are Thread.MIN_PRIORITY (1) and Thread.MAX_PRIORITY
(10), respectively. The default priority is Thread.NORM_PRIORITY (5).
 Priority is not guaranteed, as it depends on the specific scheduler implementation of
the Java Virtual Machine (JVM).
 Priority is just a suggestion for the thread scheduler and does not always guarantee
that the thread with the highest priority will be executed first. It depends on the
internal workings of the JVM's scheduler.
Multithreading in Java – Example (1/4)
14

import java.util.Random;
class NumberGenerator implements Runnable {

private Random rand = new Random();


private int number;

@Override
public void run() {
for (int i = 1; i <= 5; i++) {
number = rand.nextInt(100);
System.out.println("Thread Producer - Generated number: " + number);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Multithreading in Java – Example (2/4)
15

class NumberProcessor implements Runnable {

@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println("Thread Handler - Square of generated number : " + (i * i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Multithreading in Java – Example (3/4)
16

public class Main {


public static void main(String[] args) {

// Create threads for both Runnable classes


Thread thread1 = new Thread(new NumberGenerator());
Thread thread2 = new Thread(new NumberProcessor());

// Start the threads


thread1.start();
thread2.start();

try {
// Ensure the main thread waits for both threads to finish before exiting
thread1.join();
thread2.join();
} catch (InterruptedException e) { e.printStackTrace(); }

System.out.println("Execution finished.");
}
}
Multithreading in Java – Example (4/4)
17

Thread Handler - Square of generated number : 1


Thread Producer - Generated number: 99
Thread Handler - Square of generated number : 4
Thread Producer - Generated number: 62
Thread Handler - Square of generated number : 9
Thread Producer - Generated number: 25
Thread Handler - Square of generated number : 16
Thread Handler - Square of generated number : 25
Thread Producer - Generated number: 83
Thread Producer - Generated number: 47
Execution finished.
Daemon Threads (1/4)
18

 A daemon thread is a special type of thread that runs in the background and does
not keep the main program alive if it is the only active thread remaining.
 Regular threads handle the main tasks of the application such as data processing or
user input, while daemon threads run background services such as garbage collection,
monitoring, or logging.
 The JVM continues running as long as at least one regular thread is active. It can
terminate even if one or more daemon threads are still running.
 By default, all threads are regular. To create a daemon thread, you must explicitly
call setDaemon(true) before starting the thread.
Daemon Threads (2/4)
19

import java.util.Scanner;
class UserInputGetter implements Runnable {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter your name: ");
String name = scanner.nextLine();
System.out.println("Hello " + name);
scanner.close();
}
}
class Timer implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("Time's up!");
System.exit(0);
}
}
Daemon Threads (3/4)
20

public class Main {


public static void main(String[] args) {
Thread timer = new Thread(new Timer());
Thread userin = new Thread(new UserInputGetter());
timer.setDaemon(true);
System.out.println("You have 5 seconds to enter your name");
timer.start();
userin.start();
}
}
Daemon Threads (4/4)
21

public class Main {


public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Thread timer = new Thread(new Timer());
timer.setDaemon(true);
System.out.println("You have 5 seconds to enter your name");
System.out.print("Enter your name: ");
timer.start();
String name = scanner.nextLine();
System.out.println("Hello " + name);
scanner.close();
}
}
Thread Synchronization
22

 When multiple threads share resources (variables or instruction sequences), synchronization


issues almost automatically arise ➤ They should not access them simultaneously.
 Example: consider the case of one thread trying to modify a variable while another
thread simultaneously tries to read it.
 The goal of synchronization is to ensure the consistency of shared data between threads,
avoiding concurrent access that could lead to incorrect results.
 Synchronization provides protection for shared resources or critical sections ➤ when one
thread accesses one of these resources, no other thread is allowed to access it until the first
has completed its execution.
 Acquiring an object's lock is costly. To avoid performance degradation, critical sections
should be short and used appropriately. A call to a synchronized method is four times more
expensive.
 Synchronization in Java can be achieved using two mechanisms: Monitors or Semaphores.
Thread Synchronization – Monitors (1/3)
23

 In Java, each object has its own lock or monitor.


 The monitor is an internal mechanism used by the JVM to regulate concurrent access
to object methods or other critical sections.
 The synchronized keyword is used to work with monitors in Java.
 It is possible to synchronize access to a method by using the synchronized keyword.
When a thread executes this method on an object, other threads cannot execute it on
the same object. However, other threads can execute this method on another instance
of the same class. public synchronized TYPE myMethod(...) {
// Critical section
}

 At the end of the synchronized method call, any waiting thread can be selected by
the JVM thread scheduler, which may not follow a strict order. The choice depends on:
Thread priorities, JVM scheduling policy, and OS-level thread management
Thread Synchronization – Monitors (2/3)
24

 When a block of code is marked as synchronized, it means that access to this block
is regulated by the monitor (the current object or any other object) associated with
the block. synchronized(this) {
// Critical section
}

 To ensure mutual exclusion to a method or block across all instances of a class, we use
class-level synchronization. This is essential when threads must coordinate access to
shared resources. Two main approaches are used:
 Using static synchronized methods: Public static synchronized TYPE method(…) {
// Critical section
}
 Using synchronized blocks with a shared monitor:
private static ClassObject obj = new ClassObject(); synchronized (MyClass.class) {
... // Critical section
synchronized (obj) { // Critical section } }
Thread Synchronization – Monitors (3/3)
25

 The wait() and notify() methods allow threads to cooperate. wait() puts a thread
into a waiting state until another thread calls notify() to wake it up.
 When a thread is inside a synchronized method or block and calls the wait() method,
it releases the monitor of the object it is synchronized on, allowing other threads to
access synchronized methods on the same object while this thread is waiting.
 When a thread is awakened after a call to notify() or notifyAll(), it waits to obtain
the monitor of the object again before it can resume execution.
 notify(): wakes up only one thread waiting on the object's monitor. The choice of
which thread to wake up is not predictable — it's up to the JVM scheduler.
 notifyAll(): wakes up all threads waiting on the object's monitor. Only one thread
will acquire the lock and proceed — others go back to waiting state.
 Using notify() when multiple threads are involved can lead to missed signals or
deadlocks if the awakened thread isn't the one that can proceed.
Thread Synchronization – Monitors – Example (1/4)
26
import java.util.Random;
class NumberGenerator implements Runnable {
private Random rand = new Random();
private int number;
private boolean numberProduced = false;
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
synchronized(this) {
number = rand.nextInt(100);
System.out.println("Thread Producer - Generated number: " + number);
numberProduced = true;
this.notify();
}
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
public synchronized int getGeneratedNumber() throws InterruptedException {
while (!numberProduced) this.wait();
numberProduced = false;
return number;
}
}
Thread Synchronization – Monitors – Example (2/4)
27

class NumberProcessor implements Runnable {

private NumberGenerator generator;

public NumberProcessor(NumberGenerator generator) {


this.generator = generator;
}

@Override
public void run() {
for (int i = 1; i <= 5; i++) {
try {
int number = generator.getGeneratedNumber();
System.out.println("Thread Handler - Square of generated number: " + (number * number));
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
Thread Synchronization – Monitors – Example (3/4)
28

public class MonitorSyncExample {

public static void main(String[] args) {


NumberGenerator generator = new NumberGenerator();
Thread generatorThread = new Thread(generator);
Thread processorThread = new Thread(new NumberProcessor(generator));

generatorThread.start();
processorThread.start();

try {
generatorThread.join();
processorThread.join();
} catch (InterruptedException e) { e.printStackTrace(); }

System.out.println("Execution finished.");
}
}
Thread Synchronization – Monitors – Example (4/4)
29

Thread Producer - Generated number: 54


Thread Handler - Square of generated number: 2916
Thread Producer - Generated number: 73
Thread Handler - Square of generated number: 5329
Thread Producer - Generated number: 97
Thread Handler - Square of generated number: 9409
Thread Producer - Generated number: 80
Thread Handler - Square of generated number: 6400
Thread Producer - Generated number: 1
Thread Handler - Square of generated number: 1
Execution finished.
Thread Synchronization - Semaphores
30

 Monitors are high-level synchronization structures that allow for mutual exclusion,
passive waiting, etc., without the need for mutexes or semaphores.
 Semaphores can be used to regulate access to a set of shared resources and are
useful for controlling concurrent access in scenarios where a limited number of threads
can simultaneously access certain resources.
 In Java, the Semaphore class is part of the java.util.concurrent package and
provides an implementation of a semaphore.
 With semaphores, threads attempt to acquire the semaphore using the acquire()
method, perform critical operations, and then release the semaphore using the
release() method. This allows for controlling concurrent access to shared resources.
Thread Synchronization – Semaphores – Example
31
(1/3)
import java.util.Random;
import java.util.concurrent.Semaphore;
class NumberGenerator implements Runnable {
private Random rand = new Random();
private int number;
private final Semaphore production = new Semaphore(1);
private final Semaphore consumption = new Semaphore(0);
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
try { production.acquire();
number = rand.nextInt(100);
System.out.println("Thread Producer - Generated number: " + number);
consumption.release();
} catch (InterruptedException e) { e.printStackTrace(); }
try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
}
}
public int getGeneratedNumber() throws InterruptedException {
consumption.acquire();
int result = this.number;
production.release();
return result;
}
}
Thread Synchronization – Semaphores – Example
32
(2/3)
class NumberProcessor implements Runnable {

private NumberGenerator generator;

public NumberProcessor(NumberGenerator generator) {


this.generator = generator;
}

@Override
public void run() {
for (int i = 1; i <= 5; i++) {
try {
int number = generator.getGeneratedNumber();
System.out.println("Thread Handler - Square of generated number: " + (number * number));
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
Thread Synchronization – Semaphores – Example
33
(3/3)
public class SemaphoreSyncExample {
public static void main(String[] args) {
NumberGenerator generator = new NumberGenerator();
Thread generatorThread = new Thread(generator);
Thread processorThread = new Thread(new NumberProcessor(generator));

generatorThread.start();
processorThread.start(); Thread Producer - Generated number: 97
Thread Handler - Square of generated number: 9409
try { Thread Producer - Generated number: 19
generatorThread.join(); Thread Handler - Square of generated number: 361
processorThread.join(); Thread Producer - Generated number: 84
Thread Handler - Square of generated number: 7056
} catch (InterruptedException e) { Thread Producer - Generated number: 2
e.printStackTrace(); Thread Handler - Square of generated number: 4
} Thread Producer - Generated number: 54
Thread Handler - Square of generated number: 2916
System.out.println("Execution finished."); Execution finished.
}
}
34 Distributed Objects
Introduction to Object Distribution
35

 Object distribution is the process of deploying software objects across multiple


machines or network nodes. It enables objects to communicate and collaborate
remotely as if they were local.
 Benefits of distributing objects:
 Resource sharing: access data or services hosted elsewhere.

 Load balancing: distribute processing to optimize performance.

 Scalability: Add more machines to handle increased load.

 Distribution of objects has several impacts and requirements:


 Introduces latency and failure considerations.

 Needs security mechanisms (e.g., authentication, authorization, etc.).

 Uses serialization and marshalling for data transfer and proxies for communication.

 Sockets and RMI are two fundamental techniques for enabling object distribution.
36 Distributed Objects: Sockets
Low-level Communication
Sockets Mechanism (1/3)
37

 Sockets are a software abstraction that enables bidirectional communication (send


and receive data simultaneously) between two programs on a network.
 Sockets are widely used for developing distributed applications such as web servers,
messaging services, and many others.
 Sockets are commonly used in client-server architectures, where one program acts as
a server listening for incoming connections, and other programs act as clients connecting
to the server. But it can also be used for P2P architectures when each peer can act,
simultaneously, as a client and a server.
 Communication through sockets typically relies on specific protocols:
 TCP (Transmission Control Protocol): reliable, connection-oriented communication.

 UDP (User Datagram Protocol): lighter communication mechanism.


Sockets Mechanism (2/3)
38

 Using sockets in UDP mode involves some differences compared to using TCP sockets:
 UDP is simpler and faster than TCP — no handshake and minimal overhead.

 Unlike TCP, UDP does not ensure packet delivery or ordering nor data completeness
but reliability isn't ignored — it's offloaded to developers.
 UDP is ideal for applications when timeliness matters more than reliability:

◼ VoIP (e.g., Skype, Zoom) where missing a word is better than delayed audio
◼ Live video streaming where a dropped frame is OK — delay isn't
◼ Online gaming where real-time reaction matters more than every update
 There are two main types of sockets:
 Server-side sockets: listens for incoming connections.

 Client-side sockets: used by clients to connect to the server.


Sockets Mechanism (3/3)
39

 Sockets are identified by and bound to during their creation:


 IP address: identifies the host.

 Port number: specifies the service or application on that host.

 Once a connection is established between the client and the server, data streams can
be created on both sides to allow reading and writing data over the connection.
 The lifecycle of communication via sockets typically involves:
1. Create sockets – The server creates a server socket to listen for connections,
while the client creates a socket to initiate a connection.
2. Establishing a connection – The server accepts incoming connections, and the
client connects to the server.
4. Data exchange – Client and server communicate through streams of their sockets.
5. Closing the connection – Once communication is complete, both sides close their
streams and sockets to release resources.
Sockets Types
40
Sockets in Java
41

 In Java, socket management is facilitated by the java.net package, which provides


classes for creating network applications, including communication via sockets.
 The main classes for socket management in Java are:
 Socket: Used by clients to connect to a server in TCP mode.

 ServerSocket: Used by servers to listen for incoming TCP connections.

 DatagramSocket: Used for sending and receiving datagrams in UDP mode.

 Using sockets in UDP mode in java involves some differences compared to using TCP
sockets.
Sockets in Java – TCP Mode (1/3)
42

 To create a client socket in Java, an object of the Socket class is instantiated by


providing the IP address and port of the server to which you wish to connect.

Socket clientSocket = new Socket(server_IP, server_listening_port);

 The range of ports in Java for TCP and UDP sockets is from 0 to 65535.
◼ Ports 0 to 1023 (reserved ports) are typically reserved for system services.
◼ Ports 1024 to 49151 (registered ports) can be used by user applications.
◼ Ports 49152 to 65535 (private ports) are generally used for temporary
connections.
 If no port is specified when creating a socket, the operating system assigns a
temporary (private) port as the source port for the outgoing connection.
Sockets in Java – TCP Mode (2/3)
43

 To create a server socket in Java, an object of the ServerSocket class is instantiated by


specifying the port on which the server will listen for incoming connections.
ServerSocket serverSocket = new ServerSocket(server_listening_port);

 To accept incoming connections on the server side, the accept() method of the
ServerSocket object is used. This method returns a Socket object representing the
connection established with a client.
Socket clientSocket = serverSocket.accept();

 Once a connection is established, data streams associated with the socket can be
obtained for communication. InputStream and OutputStream are commonly used to
read and write data.
InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream();
Sockets in Java – TCP Mode (3/3)
44

 We use the input and output stream methods to exchange data between the client and
the server.
// Reading from the server side
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String clientMessage = reader.readLine();

// Writing in the server side


PrintWriter writer = new PrintWriter(outputStream, true);
writer.println(message_from_the_Server);

 Once the communication is complete, it is important to close the sockets to release


resources.
clientSocket.close();
serverSocket.close();
Sockets in Java – Mode TCP – Example (1/2)
45

import java.io.*; import java.net.*;


class TCPClient {
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 12345);
System.out.println("Connected to the server.");

// Reading and writing streams to communicate with the server


BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);

// Sending a message to the server


writer.println("Hello from Client!");

// Reading data from the server


String serverMessage = reader.readLine();
System.out.println("Received from server: " + serverMessage);

// Closing streams
reader.close(); writer.close(); socket.close();
} catch (IOException e) { e.printStackTrace(); }
}
}
Sockets in Java – Mode TCP – Example (2/2)
46

import java.io.*; import java.net.*;


class TCPServer {
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(12345);
System.out.println("Server is listening on port 12345...");
// Waiting data from the client
Socket socket = serverSocket.accept();
System.out.println("Client connected.");
// Initialize streams for reading and writing
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
// Reading data from the client
String clientMessage = reader.readLine();
System.out.println("Received from client: " + clientMessage);
// Sending data to the client
writer.println("Hello from Server!");
// Closing streams
reader.close(); writer.close(); socket.close(); serverSocket.close();
} catch (IOException e) { e.printStackTrace(); }
}
}
Sockets in Java – UDP Mode (Client Side)
47

 DatagramSocket is used to create a socket that will be utilized for sending UDP
datagrams (packets) to the server.
DatagramSocket socket = new DatagramSocket();

 The data to be sent must first be converted into bytes before being encapsulated by a
DatagramPacket.
byte[] data = message_to_be_sent.getBytes();
InetAddress serverAddress = InetAddress.getByName(server_ip);
DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, server_port);

 To send a DatagramPacket, the send() method of the DatagramSocket object is used,


providing the DatagramPacket to be sent.
socket.send(packet);

 As with TCP sockets, it is important to close the DatagramSocket after use.


socket.close();
Sockets in Java – UDP Mode (Server Side)
48

 DatagramSocket is used to create a socket for receiving UDP datagrams (packets)


from the client. You need to specify the port for this.
DatagramSocket socket = new DatagramSocket(server_port);

 To receive a datagram, create an empty DatagramPacket to hold the incoming data,


then use the receive() method of the DatagramSocket object.
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.receive(packet);

 Once the datagram is received, you can extract the data from the DatagramPacket
similarly to how you read/write using streams in TCP.
String message = new String(packet.getData(), 0, packet.getLength());

 As with TCP sockets, it is important to close the DatagramSocket after use.


socket.close();
Sockets in Java – UDP Mode – Example (1/2)
49

import java.net.*;
class UDPClient {
public static void main(String[] args) {
try {
// Create a DatagramSocket
DatagramSocket socket = new DatagramSocket();
// Specify the server address and port
InetAddress serverAddress = InetAddress.getByName("localhost");
int serverPort = 9876;
// Create the message to send
String message = "Hello, Server!";
// Convert the message into a byte array
byte[] buffer = message.getBytes();
// Create a DatagramPacket to send the data
DatagramPacket packet = new DatagramPacket(buffer, buffer.length, serverAddress, serverPort);
// Send the packet
socket.send(packet);
// Close the DatagramSocket
socket.close();
} catch (Exception e) { e.printStackTrace(); }
}
}
Sockets in Java – UDP Mode – Example (2/2)
50

import java.net.*;
class UDPServer {
public static void main(String[] args) {
try {
// Create a DatagramSocket to listen on port 9876
DatagramSocket socket = new DatagramSocket(9876);
System.out.println("Server is listening on port 9876...");
// Create a buffer for receiving data
byte[] buffer = new byte[1024];
// Create a DatagramPacket to hold incoming data
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
// Receive a packet
socket.receive(packet);
System.out.println("Packet received!");
// Extract the data and display it
String receivedMessage = new String(packet.getData(), 0, packet.getLength());
System.out.println("Message from client: " + receivedMessage);
// Close the DatagramSocket
socket.close();
} catch (Exception e) { e.printStackTrace(); }
}
}
51 Distributed Objects : RMI
RMI = Remote Method Invocation
High-level Abstraction
RMI Mechanism (1/2)
52

 RMI (Remote Method Invocation) is a mechanism that facilitates communication


between objects distributed across different machines in a Java environment.
 It offers a transparent way to invoke remote methods, simplifying the development of
distributed applications in Java ➤ It allows methods of remote objects to be invoked
as if these objects were local.
 RMI supports security mechanisms such as authentication, authorization, and data
encryption to protect remote communications and calls.
 RMI relies on the principle of serialization. Remote objects must be serializable to be
used in RMI and the parameters and return values of remote methods must be of
serializable types.
 For a remote object to be accessible by other clients, it must be registered in the RMI
Registry. This is typically done on the server side. Clients can look up these remote
objects in the registry.
RMI Mechanism (2/2)
53

 Communication via RMI between two remote objects is facilitated by proxies:


 Stub (Client side):  Skeleton/Reflection (Server Side) :
◼ Represents the remote object on the ◼ Receives requests from the client-side
client side – generated automatically. stub.
◼ Provides a local interface similar to ◼ Deserializes the data, extracts the
that of the remote object. parameters of the invoked remote
◼ Handles the serialization of method, and calls the method on the
parameters, sending the request to actual remote object.
the server, receiving the results, and ◼ Serializes the results (or exceptions) and
deserialization. sends them back to the client-side stub.
◼ Hides the details of remote ◼ Link the stub and the actual remote
communication from the client. object.
RMI Architecture
54

Client object Application Layer Server object

Stub Proxy Layer Skeleton/Reflection


Remote Reference Layer (RMI Registry)
Transport Layer
RMI in Java – Definition of the Remote Interface
55

 First, you need to define a Java interface that exposes the methods you want to be
called remotely.
 This interface must extend java.rmi.Remote, and each method must declare throws
RemoteException.

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteInterface extends Remote {


TYPE remoteMethod() throws RemoteException;
}
RMI in Java – Implementation of the Remote Object
(1/2)
56

 Implement the remote interface on the server. The objects on the server side that
implement this interface are the remote objects.

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RemoteObjectClass extends UnicastRemoteObject implements RemoteInterface {


public RemoteObjectClass throws RemoteException {
// Construtur
}

@Override
public TYPE remoteMethod() throws RemoteException {
//Do somthing and return the intended result;
}
}
RMI in Java – Implementation of the Remote Object
(2/2)
57

 If the remoteObjectClass needs to inherit from another user-defined class, you can
overcome Java’s single inheritance limitation by using delegation — manually export
the object using UnicastRemoteObject.exportObject() instead of extending
UnicastRemoteObject.
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class RemoteObjectClass extends AnyOtherClass implements RemoteInterface {


public RemoteObjectClass throws RemoteException {
UnicastRemoteObject.exportObject(this, 0)
}

@Override
public TYPE remoteMethod() throws RemoteException {
//Do somthing and return the intended result;
}
}
RMI in Java – Registering the Remote Object (Server)
58

 The remote object must be registered in the RMI registry so that it can be found by
clients.

import java.rmi.Naming;

public class RemoteServer {


public static void main(String[] args) {
try {

RemoteInterface remoteObject = new RemoteObjectClass();


Naming.rebind(remote_object_name, remoteObject);

} catch (Exception e) {
e.printStackTrace();
}
}
}
RMI in Java – Calling the Remote Object (Client)
59

 The client can search for and call methods on the remote object using the automatically
generated stub.

import java.rmi.Naming;

public class RemoteClient {


public static void main(String[] args) {
try {
RemoteInterface remoteObject =
(RemoteInterface) Naming.lookup("rmi://" + server_ip + "/" + "remote_object_name");
TYPE result = remoteObject.remoteMethod();
} catch (Exception e) {
e.printStackTrace();
}
}
}
RMI in Java – Execution Process
60

 Launch RMI Registry


 Open the terminal.

 Use the cd command to navigate to the directory where your compiled class files
(.class) are located.
 Run the rmiregistry command.

 Launch the class modeling the Server.


 Launch the class modeling the Client.
 When the classes are saved in a package other than the default one in an Eclipse
project:
 Use the full name of the server class when registering in the RMI registry.

 Use the full URL when looking up the remote object.


RMI in Java – Example (1/2)
61

import java.rmi.Remote;
import java.rmi.RemoteException;

// Remote Interface
public interface Calculator extends Remote {
int add(int a, int b) throws RemoteException;
}

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

// Distant Object class definition implementing the remote interface


public class CalculatorImpl extends UnicastRemoteObject implements Calculator {

public CalculatorImpl() throws RemoteException {}

@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
RMI in Java – Example (2/2)
62

import java.rmi.Naming;
public class CalculatorServer {
public static void main(String[] args) {
try {
Calculator calculator = new CalculatorImpl();
Naming.rebind("CalculatorService", calculator);
System.out.println("Calculator server is running...");
} catch (Exception e) { e.printStackTrace(); }
}
}

public class CalculatorClient {


public static void main(String[] args) {
try {
Calculator calculator = (Calculator) Naming.lookup("rmi://localhost/CalculatorService");
int result = calculator.add(5, 10);
System.out.println("The result of adding 5 and 10 is: " + result);
} catch (Exception e) { e.printStackTrace(); }
}
}
Sockets vs RMI
63

Usage of Sockets Usage of RMI


Allow direct communication between distributed Relies on remote method invocation. Clients call
applications by transferring byte streams. methods of remote objects as if they were local.
Object serialization must be handled manually by Serialization is handled automatically by RMI.
converting objects into byte sequences before Remote objects only need to implement the
sending them and reconstructing them upon arrival. Serializable interface.
Applications using sockets must define their own Remote objects expose interfaces that define the
communication protocol, specifying how the data methods that can be called remotely.
will be structured and interpreted.
Applications using sockets must manually handle the Clients obtain RMI proxies, which act as local
opening, closing, and management of connections. representatives of remote objects. RMI manages the
creation and management of connections.
64 The End.

You might also like