230 lines
5.6 KiB
Markdown
230 lines
5.6 KiB
Markdown
# Advanced Programming Assignment 8
|
||
|
||
|
||
* Name: *Hesam Ghazi*
|
||
* Student Number: *403222015*
|
||
* Course: *Advanced Programming*
|
||
|
||
---
|
||
|
||
# Theoretical Questions
|
||
|
||
## 1. `start()` vs `run()`
|
||
|
||
### Question 1: What output do you get from the program? Why?
|
||
|
||
The program prints something similar to the following:
|
||
|
||
```
|
||
Calling run()
|
||
Running in: main
|
||
Calling start()
|
||
Running in: Thread-2
|
||
```
|
||
|
||
When `t1.run()` is called, the `run()` method executes like a normal Java method. It does **not** create a new thread, so it runs inside the current thread, which is the **main** thread.
|
||
|
||
When `t2.start()` is called, the Java Virtual Machine creates a **new thread**, and that thread executes the `run()` method independently. Therefore, `Thread.currentThread().getName()` returns `"Thread-2"`.
|
||
|
||
---
|
||
|
||
### Question 2: What's the difference in behavior between calling `start()` and `run()`?
|
||
|
||
| `start()` | `run()` |
|
||
| -------------------------------------------------- | ------------------------------------------- |
|
||
| Creates a new thread. | Does not create a new thread. |
|
||
| Executes `run()` concurrently. | Executes `run()` like a normal method call. |
|
||
| The JVM schedules the new thread. | Runs immediately in the current thread. |
|
||
| Allows multiple threads to execute simultaneously. | No parallelism or concurrency occurs. |
|
||
|
||
For example:
|
||
|
||
```java
|
||
Thread thread = new Thread(new MyRunnable());
|
||
|
||
thread.run(); // Executes on the current thread (main)
|
||
thread.start(); // Executes on a new thread
|
||
```
|
||
|
||
Using `start()` is essential when we want to perform tasks concurrently.
|
||
|
||
---
|
||
|
||
# 2. Daemon Threads
|
||
|
||
### Question 1: What output do you get from the program? Why?
|
||
|
||
A typical output is:
|
||
|
||
```
|
||
Main thread ends.
|
||
Daemon thread running...
|
||
Daemon thread running...
|
||
```
|
||
|
||
Sometimes only the following is printed:
|
||
|
||
```
|
||
Main thread ends.
|
||
```
|
||
|
||
This happens because daemon threads are **background threads**. When the main (user) thread finishes and no other user threads remain, the JVM automatically terminates all daemon threads, even if they have not completed their work.
|
||
|
||
Since the daemon thread sleeps for 500 ms in every iteration, it may be terminated before printing all twenty messages.
|
||
|
||
---
|
||
|
||
### Question 2: What happens if you remove `thread.setDaemon(true)`?
|
||
|
||
If `thread.setDaemon(true)` is removed, the thread becomes a **user thread**.
|
||
|
||
In this case, the JVM waits until the thread completes all twenty iterations before terminating the program.
|
||
|
||
The output becomes similar to:
|
||
|
||
```
|
||
Main thread ends.
|
||
Daemon thread running...
|
||
Daemon thread running...
|
||
...
|
||
Daemon thread running...
|
||
```
|
||
|
||
The program will continue running for approximately **10 seconds** (20 × 500 ms).
|
||
|
||
---
|
||
|
||
### Question 3: What are some real-life use cases of daemon threads?
|
||
|
||
Daemon threads are commonly used for background services that support the application but are not essential to its primary functionality.
|
||
|
||
Examples include:
|
||
|
||
* Garbage collection
|
||
* Automatic cache cleanup
|
||
* Logging services
|
||
* Monitoring system resources
|
||
* Background file synchronization
|
||
* Session timeout checking
|
||
* Periodic health checks
|
||
* Scheduled maintenance tasks
|
||
|
||
These tasks should stop automatically when the application exits, making daemon threads an appropriate choice.
|
||
|
||
---
|
||
|
||
# 3. A Shorter Way to Create Threads
|
||
|
||
### Question 1: What output do you get from the program?
|
||
|
||
The program prints:
|
||
|
||
```
|
||
Thread is running using a ...!
|
||
```
|
||
|
||
This message is printed by the newly created thread after `thread.start()` is called.
|
||
|
||
---
|
||
|
||
### Question 2: What is the `() -> { ... }` syntax called?
|
||
|
||
The syntax
|
||
|
||
```java
|
||
() -> {
|
||
...
|
||
}
|
||
```
|
||
|
||
is called a **Lambda Expression**.
|
||
|
||
Lambda expressions were introduced in **Java 8** as a concise way to implement functional interfaces such as `Runnable`.
|
||
|
||
The previous code
|
||
|
||
```java
|
||
Thread thread = new Thread(() -> {
|
||
System.out.println("Thread is running using a ...!");
|
||
});
|
||
```
|
||
|
||
is equivalent to
|
||
|
||
```java
|
||
Thread thread = new Thread(new Runnable() {
|
||
@Override
|
||
public void run() {
|
||
System.out.println("Thread is running using a ...!");
|
||
}
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
### Question 3: How is this code different from creating a class that extends `Thread` or implements `Runnable`?
|
||
|
||
There are three common approaches for creating a thread.
|
||
|
||
### 1. Extending `Thread`
|
||
|
||
```java
|
||
class MyThread extends Thread {
|
||
@Override
|
||
public void run() {
|
||
System.out.println("Running...");
|
||
}
|
||
}
|
||
```
|
||
|
||
**Advantages**
|
||
|
||
* Easy for simple examples.
|
||
|
||
**Disadvantages**
|
||
|
||
* Java supports only single inheritance.
|
||
* The task is tightly coupled with the thread.
|
||
|
||
---
|
||
|
||
### 2. Implementing `Runnable`
|
||
|
||
```java
|
||
class MyRunnable implements Runnable {
|
||
@Override
|
||
public void run() {
|
||
System.out.println("Running...");
|
||
}
|
||
}
|
||
```
|
||
|
||
**Advantages**
|
||
|
||
* Better object-oriented design.
|
||
* Allows inheritance from another class.
|
||
* The task and the thread are separated.
|
||
|
||
---
|
||
|
||
### 3. Using a Lambda Expression
|
||
|
||
```java
|
||
Thread thread = new Thread(() -> {
|
||
System.out.println("Running...");
|
||
});
|
||
```
|
||
|
||
**Advantages**
|
||
|
||
* Very concise and readable.
|
||
* Eliminates unnecessary boilerplate code.
|
||
* Ideal for short tasks.
|
||
* Still implements the `Runnable` interface internally.
|
||
|
||
---
|
||
|
||
## Conclusion
|
||
|
||
Lambda expressions are the preferred choice for short and simple thread tasks because they produce cleaner and more maintainable code. For larger or reusable tasks, implementing `Runnable` is generally considered the best practice due to better separation of responsibilities. Extending `Thread` is the least flexible approach and is usually reserved for special cases.
|