[go: up one dir, main page]

0% found this document useful (0 votes)
13 views9 pages

Ass 5 Java

The document contains an assignment with multiple questions related to Java multithreading concepts, including the differences between Thread and Runnable, thread creation, synchronization to avoid race conditions, advantages of HashMap, producer-consumer problem using wait and notify, and deadlocks. Each question is followed by a detailed solution with code examples and explanations. The document emphasizes the importance of thread management, synchronization, and data structures in Java programming.
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)
13 views9 pages

Ass 5 Java

The document contains an assignment with multiple questions related to Java multithreading concepts, including the differences between Thread and Runnable, thread creation, synchronization to avoid race conditions, advantages of HashMap, producer-consumer problem using wait and notify, and deadlocks. Each question is followed by a detailed solution with code examples and explanations. The document emphasizes the importance of thread management, synchronization, and data structures in Java programming.
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/ 9

Assignment 5

Note: Attempt any two questions from the following [5 Marks Each]

Q1: Explain the difference between the Thread class and the Runnable interface in Java.
Provide examples of situations where you might choose one approach over the other.

SOLUTION : In Java, `Thread` and `Runnable` are two ways to create and manage
threads. Here's the difference between them:

# Thread Class:
- A `Thread` is a class that represents a separate ow of execution in a program.
- To create a new thread using the `Thread` class, you need to extend the `Thread` class
and override the `run()` method.
- The `Thread` class provides methods to control the thread's execution, such as `start()`,
`join()`, and `interrupt()`.

# Runnable Interface:
- A `Runnable` is an interface that represents a task that can be executed by a thread.
- To create a new thread using the `Runnable` interface, you need to implement the
`Runnable` interface and provide an implementation for the `run()` method.
- A `Runnable` object can be passed to a `Thread` object, which will execute the `run()`
method.

# Key Differences:
1. *Inheritance*: When you extend the `Thread` class, you can't extend any other class.
When you implement the `Runnable` interface, you can still extend other classes.
2. *Flexibility*: The `Runnable` interface provides more exibility, as you can pass a
`Runnable` object to different types of executors or threads.
3. *Reusability*: A `Runnable` object can be reused in different threads, whereas a
`Thread` object is tightly coupled to its execution.

# Choosing Between Thread and Runnable:


1. *Use Thread*: When you need to control the thread's execution directly, such as when
you need to use thread-speci c methods like `join()` or `interrupt()`.
2. *Use Runnable*: When you want to decouple the task from the thread execution, or
when you need to pass the task to different types of executors or threads.

# Examples:
*Thread Example:*
```
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello from thread!");
}

public static void main(String[] args) {


MyThread thread = new MyThread();
thread.start();
}
}
```
fi
fl
fl
Assignment 5

*Runnable Example:*
```
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello from runnable!");
}

public static void main(String[] args) {


MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
```

Q2: Write a Java program that demonstrates the creation of a thread by extending the
Thread class. Include a simple task for the thread to execute.

SOLUTION: Here's a Java program that demonstrates the creation of a thread by


extending the `Thread` class:

# Creating a Thread by Extending Thread Class:


```
// De ne a class that extends Thread
class MyThread extends Thread {
private String threadName;

public MyThread(String threadName) {


this.threadName = threadName;
}

@Override
public void run() {
// Task to be executed by the thread
System.out.println(threadName + " is starting.");
for (int i = 1; i <= 5; i++) {
System.out.println(threadName + " is running iteration " + i);
try {
Thread.sleep(1000); // Sleep for 1 second
} catch (InterruptedException e) {
System.out.println(threadName + " was interrupted.");
}
}
System.out.println(threadName + " has nished.");
}
}

public class Main {


fi
fi
Assignment 5
public static void main(String[] args) {
// Create two threads
MyThread thread1 = new MyThread("Thread-1");
MyThread thread2 = new MyThread("Thread-2");

// Start the threads


thread1.start();
thread2.start();
}
}
```

# Explanation:
1. *De ne a class that extends Thread*: We de ne a class `MyThread` that extends the
`Thread` class.
2. *Override the run() method*: We override the `run()` method to de ne the task that the
thread will execute.
3. *Create and start threads*: In the `main()` method, we create two instances of
`MyThread` and start them using the `start()` method.

# Output:
The output will show both threads running concurrently, with each thread printing its name
and iteration number. The exact output may vary due to the concurrent nature of thread
execution.

# Key Points:
1. *Thread creation*: Threads are created by extending the `Thread` class and overriding
the `run()` method.
2. *Thread execution*: Threads are executed by calling the `start()` method, which calls
the `run()` method internally.
3. *Concurrency*: Multiple threads can run concurrently, improving responsiveness and
system utilization.

Note: Attempt any two questions from the following [10 Marks Each]

Q4: Implement a Java program that shows the use of synchroniSation to avoid race
conditions. Provide a brief explanation of how synchronisation is achieved in your program

SOLUTION : Here's a Java program that demonstrates the use of synchronization to avoid
race conditions:

# Synchronized Program:
```
class Counter {
private int count = 0;

// Synchronized method to increment count


public synchronized void increment() {
count++;
fi
fi
fi
Assignment 5
}

// Synchronized method to get count


public synchronized int getCount() {
return count;
}
}

class CounterThread extends Thread {


private Counter counter;

public CounterThread(Counter counter) {


this.counter = counter;
}

@Override
public void run() {
for (int i = 0; i < 100000; i++) {
counter.increment();
}
}
}

public class Main {


public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();

// Create two threads


CounterThread thread1 = new CounterThread(counter);
CounterThread thread2 = new CounterThread(counter);

// Start the threads


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

// Wait for both threads to nish


thread1.join();
thread2.join();

// Print the nal count


System.out.println("Final count: " + counter.getCount());
}
}
```

# Explanation:
1. *Synchronized methods*: We declare the `increment()` and `getCount()` methods in the
`Counter` class as `synchronized`. This ensures that only one thread can execute these
methods at a time.
2. *Mutex lock*: When a thread calls a `synchronized` method, it acquires a mutex lock on
the object. Other threads cannot call `synchronized` methods on the same object until the
lock is released.
fi
fi
Assignment 5
3. *Thread safety*: By synchronizing access to the `count` variable, we ensure that the
increment operation is atomic and thread-safe.

# Bene ts:
1. *Prevents race conditions*: Synchronization prevents multiple threads from accessing
shared data simultaneously, avoiding race conditions.
2. *Ensures data consistency*: Synchronization ensures that shared data is consistent and
accurate.

# Key Points:
1. *Use synchronized keyword*: Use the `synchronized` keyword to declare methods or
blocks that require exclusive access to shared data.
2. *Locking mechanism*: Synchronization uses a locking mechanism to ensure that only
one thread can access shared data at a time.
3. *Thread safety*: Synchronization is essential for ensuring thread safety in multithreaded
programs.

Q5: Discuss the advantages of using the HashMap class in Java's Collections Framework.
Provide examples of situations where a HashMap is more suitable than other map
implementations.

SOLUTION : The `HashMap` class in Java's Collections Framework offers several


advantages:

# Advantages of HashMap:
1. *Fast lookup and insertion*: `HashMap` provides fast lookup, insertion, and deletion
operations with an average time complexity of O(1), making it suitable for large datasets.
2. *Ef cient storage*: `HashMap` stores key-value pairs ef ciently, using a hash table data
structure that minimizes memory usage.
3. *Flexible key and value types*: `HashMap` allows any non-null object as a key and any
object as a value, providing exibility in data storage and retrieval.

# Situations where HashMap is more suitable:


1. *Caching*: `HashMap` is suitable for caching frequently accessed data, as it provides
fast lookup and insertion operations.
2. *Data aggregation*: `HashMap` can be used to aggregate data from various sources,
such as counting occurrences of words in a text or aggregating data by category.
3. *Con guration storage*: `HashMap` can be used to store con guration data, such as
user preferences or application settings.

# Comparison with other map implementations:


1. *TreeMap*: `TreeMap` provides a sorted map implementation, which is useful when
data needs to be ordered or sorted. However, `TreeMap` has a slower lookup time
complexity (O(log n)) compared to `HashMap`.
2. *LinkedHashMap*: `LinkedHashMap` provides a map implementation that preserves the
order of insertion, which is useful when data needs to be processed in a speci c order.
However, `LinkedHashMap` may have slower performance compared to `HashMap` due to
the additional overhead of maintaining the linked list.

# Example usage:
```
fi
fi
fi
fl
fi
fi
fi
Assignment 5
import java.util.HashMap;
import java.util.Map;

public class HashMapExample {


public static void main(String[] args) {
// Create a HashMap
Map<String, Integer> studentGrades = new HashMap<>();

// Insert data into the HashMap


studentGrades.put("John", 85);
studentGrades.put("Alice", 90);
studentGrades.put("Bob", 78);

// Retrieve data from the HashMap


System.out.println("John's grade: " + studentGrades.get("John"));

// Update data in the HashMap


studentGrades.put("John", 88);
System.out.println("John's updated grade: " + studentGrades.get("John"));
}
}
```

Note: Attempt any two questions from the following [20 Marks Each]

Q7: Design and implement a multithreaded Java program that demonstrates thread
communication using wait(), notify(), or notifyAll(). Provide a detailed explanation of how the
threads communicate

SOLUTION: Here's a Java program that demonstrates thread communication using `wait()`,
`notify()`, and `notifyAll()`:

# Producer-Consumer Problem:
```
import java.util.LinkedList;
import java.util.Queue;

class SharedQueue {
private Queue<Integer> queue = new LinkedList<>();
private nal int MAX_SIZE = 5;

public synchronized void produce(int item) throws InterruptedException {


while (queue.size() == MAX_SIZE) {
System.out.println("Queue is full. Producer is waiting.");
wait();
}
queue.add(item);
System.out.println("Produced: " + item);
notifyAll(); // Notify consumers that an item is available
}

public synchronized int consume() throws InterruptedException {


fi
Assignment 5
while (queue.isEmpty()) {
System.out.println("Queue is empty. Consumer is waiting.");
wait();
}
int item = queue.poll();
System.out.println("Consumed: " + item);
notifyAll(); // Notify producers that space is available
return item;
}
}

class Producer extends Thread {


private SharedQueue queue;

public Producer(SharedQueue queue) {


this.queue = queue;
}

@Override
public void run() {
try {
for (int i = 1; i <= 10; i++) {
queue.produce(i);
Thread.sleep(100); // Simulate production time
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

class Consumer extends Thread {


private SharedQueue queue;

public Consumer(SharedQueue queue) {


this.queue = queue;
}

@Override
public void run() {
try {
for (int i = 1; i <= 10; i++) {
queue.consume();
Thread.sleep(100); // Simulate consumption time
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}

public class Main {


public static void main(String[] args) {
SharedQueue queue = new SharedQueue();
Producer producer = new Producer(queue);
Consumer consumer = new Consumer(queue);
Assignment 5

producer.start();
consumer.start();
}
}
```

# Explanation:
1. *SharedQueue class*: This class represents a shared queue that producers and consumers
access. It uses `wait()` and `notifyAll()` to coordinate access to the queue.
2. *Producer thread*: The producer thread produces items and adds them to the queue. If the
queue is full, it waits until space becomes available.
3. *Consumer thread*: The consumer thread consumes items from the queue. If the queue is
empty, it waits until an item becomes available.
4. *wait() and notifyAll()*: These methods are used to coordinate access to the queue. When a
producer or consumer thread is waiting, it releases the lock on the queue, allowing other threads to
access it. When an item is added or removed from the queue, the `notifyAll()` method is called to
wake up waiting threads.

# Thread Communication:
1. *Producer-consumer synchronization*: The producer and consumer threads synchronize access
to the shared queue using `wait()` and `notifyAll()`.
2. *Queue full or empty*: When the queue is full or empty, the producer or consumer thread waits
until the condition changes.
3. *Noti cation*: When an item is added or removed from the queue, the `notifyAll()` method is
called to wake up waiting threads.

Q8: Explore the concepts of deadlocks in multithreading. Explain the key characteristics of
deadlocks and provide strategies for deadlock prevention and handling. Include examples to
support your explanations.

SOLUTION: Deadlocks are a fundamental problem in multithreading that can cause a program to
freeze or become unresponsive. Here's an explanation of deadlocks, their characteristics, and
strategies for prevention and handling:

# What is a Deadlock?
A deadlock is a situation where two or more threads are blocked inde nitely, each waiting for the
other to release a resource. This creates a cycle of dependency, where none of the threads can
proceed.

# Key Characteristics of Deadlocks:


1. *Mutual Exclusion*: Two or more threads require exclusive access to a common resource.
2. *Hold and Wait*: One thread holds a resource and waits for another resource held by another
thread.
3. *No Preemption*: The operating system does not force a thread to release a resource.
4. *Circular Wait*: A cycle of threads exists, where each thread is waiting for a resource held by
another thread.

# Strategies for Deadlock Prevention:


1. *Avoid Nested Locks*: Minimize the use of nested locks, which can lead to deadlocks.
2. *Lock Ordering*: Establish a consistent lock ordering to prevent deadlocks.
3. *Avoid Unnecessary Locks*: Only lock resources when necessary, and release locks as soon as
possible.
4. *Use Lock Timeout*: Use lock timeout mechanisms to detect and recover from deadlocks.
fi
fi
Assignment 5

# Strategies for Deadlock Handling:


1. *Deadlock Detection*: Implement deadlock detection mechanisms to identify deadlocks.
2. *Deadlock Recovery*: Develop strategies for recovering from deadlocks, such as aborting one of
the threads.
3. *Thread Priorities*: Assign priorities to threads to ensure that critical threads are not blocked.

# Example:
```
public class DeadlockExample {
private static nal Object lock1 = new Object();
private static nal Object lock2 = new Object();

public static void main(String[] args) {


Thread thread1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread 1: Holding lock 1...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (lock2) {
System.out.println("Thread 1: Holding lock 1 and lock 2...");
}
}
});

Thread thread2 = new Thread(() -> {


synchronized (lock2) {
System.out.println("Thread 2: Holding lock 2...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (lock1) {
System.out.println("Thread 2: Holding lock 1 and lock 2...");
}
}
});

thread1.start();
thread2.start();
}
}
```
fi
fi

You might also like