diff --git a/Theoretical Questions.pdf b/Theoretical Questions.pdf new file mode 100644 index 0000000..59ea28b Binary files /dev/null and b/Theoretical Questions.pdf differ diff --git a/Theoretical Questions.tex b/Theoretical Questions.tex new file mode 100644 index 0000000..eda8296 --- /dev/null +++ b/Theoretical Questions.tex @@ -0,0 +1,354 @@ +\documentclass[12pt,a4paper]{article} + +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[margin=2.5cm]{geometry} +\usepackage{listings} +\usepackage{xcolor} +\usepackage{hyperref} +\usepackage{parskip} +\usepackage{booktabs} +\usepackage{array} +\usepackage{titlesec} +\usepackage{fancyhdr} +\usepackage{amsmath} +\usepackage{enumitem} + +% ── Page style ────────────────────────────────────────────────────────────── +\pagestyle{fancy} +\fancyhf{} +\rhead{Advanced Programming – Assignment 8} +\lhead{Faraz Ardeh} +\cfoot{\thepage} + +% ── Section formatting ─────────────────────────────────────────────────────── +\titleformat{\section}{\large\bfseries}{Question \thesection.}{0.6em}{} +\titleformat{\subsection}{\normalsize\bfseries}{}{0em}{} + +% ── Java code style ───────────────────────────────────────────────────────── +\definecolor{javakw}{rgb}{0.13,0.13,0.60} +\definecolor{javastr}{rgb}{0.63,0.13,0.13} +\definecolor{javacmt}{rgb}{0.38,0.38,0.38} +\definecolor{codebg}{rgb}{0.97,0.97,0.97} +\definecolor{codefr}{rgb}{0.82,0.82,0.82} + +\lstdefinestyle{java}{ + language=Java, + basicstyle=\ttfamily\small, + keywordstyle=\color{javakw}\bfseries, + stringstyle=\color{javastr}, + commentstyle=\color{javacmt}\itshape, + numberstyle=\tiny\color{gray}, + numbers=left, + stepnumber=1, + numbersep=8pt, + backgroundcolor=\color{codebg}, + frame=single, + rulecolor=\color{codefr}, + breaklines=true, + breakatwhitespace=false, + showstringspaces=false, + tabsize=4, + captionpos=b, +} + +\lstset{style=java} + +% ── Inline code ───────────────────────────────────────────────────────────── +\newcommand{\code}[1]{\texttt{\small #1}} + +% ──────────────────────────────────────────────────────────────────────────── +\begin{document} + +% ── Title page ─────────────────────────────────────────────────────────────── +\begin{titlepage} + \centering + \vspace*{3cm} + {\Huge\bfseries Multithreading Basics\par} + \vspace{0.8cm} + {\large Assignment 8 – Theoretical Questions\par} + \vspace{0.4cm} + {\large Advanced Programming (AP)\par} + \vspace{2cm} + \rule{0.6\linewidth}{0.5pt}\\[0.4cm] + {\large\bfseries Faraz Ardeh\par} + \vspace{0.3cm} + {\normalsize fa.ardeh@gmail.com\par} + \vspace{0.6cm} + {\normalsize Shahid Beheshti University\par} + \vspace{0.3cm} + {\normalsize June 2026\par} + \vfill +\end{titlepage} + +\tableofcontents +\newpage + +% ════════════════════════════════════════════════════════════════════════════ +\section{\code{start()} vs \code{run()}} +% ════════════════════════════════════════════════════════════════════════════ + +The program under analysis: + +\begin{lstlisting}[caption={StartVsRun.java}] +public class StartVsRun { + static class MyRunnable implements Runnable { + public void run() { + System.out.println("Running in: " + Thread.currentThread().getName()); + } + } + public static void main(String[] args) throws InterruptedException { + Thread t1 = new Thread(new MyRunnable(), "Thread-1"); + System.out.println("Calling run()"); + t1.run(); + Thread.sleep(100); + + Thread t2 = new Thread(new MyRunnable(), "Thread-2"); + System.out.println("Calling start()"); + t2.start(); + } +} +\end{lstlisting} + +\subsection{Expected output} + +\begin{lstlisting}[language={},numbers=none,backgroundcolor=\color{codebg}] +Calling run() +Running in: main +Calling start() +Running in: Thread-2 +\end{lstlisting} + +\subsection{Why this output?} + +\textbf{The \code{t1.run()} call:} +Calling \code{run()} on a \code{Thread} object is just an ordinary method call, exactly +like calling any other method on any other object. +No new OS thread is created or scheduled. +The body of \code{run()} executes \emph{synchronously} inside the calling thread, +which is \code{main}. +Therefore \code{Thread.currentThread().getName()} returns \texttt{"main"}, +not \texttt{"Thread-1"}. +The thread object \texttt{t1} was constructed but its native thread was never started. + +\textbf{The \code{t2.start()} call:} +\code{start()} allocates a new OS-level thread, registers it with the JVM scheduler, +and returns to the caller \emph{immediately} (it does not wait for \code{run()} to finish). +The new thread then invokes \code{run()} autonomously and concurrently. +Inside that new thread, \code{Thread.currentThread().getName()} correctly +returns \texttt{"Thread-2"}. + +\subsection{Key differences at a glance} + +\begin{center} +\begin{tabular}{>{\bfseries}lll} +\toprule +Aspect & \code{run()} & \code{start()} \\ +\midrule +New thread created? & No & Yes (exactly one) \\ +Execution thread & Current thread & New thread \\ +Blocking behaviour & Synchronous (caller waits) & Asynchronous (returns at once) \\ +Can be called twice? & Yes, legal & No – throws \code{IllegalThreadStateException} \\ +Useful for? & Testing \code{run()} logic directly & True concurrent execution \\ +\bottomrule +\end{tabular} +\end{center} + +\textbf{Summary:} +\code{run()} is simply a method call; \code{start()} is what actually creates and +launches a new thread of execution. Calling \code{run()} instead of \code{start()} is +one of the most common multithreading bugs in Java – the code compiles and +"works", but no concurrency ever happens. + +% ════════════════════════════════════════════════════════════════════════════ +\section{Daemon Threads} +% ════════════════════════════════════════════════════════════════════════════ + +\begin{lstlisting}[caption={DaemonExample.java}] +public class DaemonExample { + static class DaemonRunnable implements Runnable { + public void run() { + for (int i = 0; i < 20; i++) { + System.out.println("Daemon thread running..."); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + // [Handling Exception...] + } + } + } + } + public static void main(String[] args) { + Thread thread = new Thread(new DaemonRunnable()); + thread.setDaemon(true); + thread.start(); + System.out.println("Main thread ends."); + } +} +\end{lstlisting} + +\subsection{Expected output (with \code{setDaemon(true)})} + +\begin{lstlisting}[language={},numbers=none,backgroundcolor=\color{codebg}] +Main thread ends. +Daemon thread running... +(program terminates -- usually 0-2 more lines, non-deterministic) +\end{lstlisting} + +\textbf{Why?} +The JVM shuts down when there are no \emph{non-daemon} threads still alive. +After \code{main()} prints \texttt{"Main thread ends."} and returns, the only surviving +thread is the daemon thread. +Because it is marked as a daemon, the JVM does \emph{not} wait for it to +finish – it exits immediately (or after one scheduling quantum), abruptly +terminating the daemon thread. +The daemon may manage to print zero, one, or a few lines before termination, +depending entirely on OS thread scheduling; this is non-deterministic. + +\subsection{What happens if you remove \code{thread.setDaemon(true)}?} + +Without that call the thread becomes a regular \emph{user thread} (the default). +The JVM rule is: \textbf{wait for all user threads to finish before exiting.} +The daemon thread (now a user thread) will sleep 500 ms per iteration and +loop 20 times – the program will take approximately $20 \times 0.5 = 10$ seconds +to terminate, and all 20 lines of \texttt{"Daemon thread running..."} will be +printed in full before the JVM exits. + +\subsection{Real-life use cases of daemon threads} + +Daemon threads are ideal for background housekeeping work that should not +prevent the application from exiting cleanly. + +\begin{enumerate}[leftmargin=2em] + \item \textbf{Garbage Collection.} + The JVM's garbage collector runs as a daemon thread; + it must not keep the JVM alive on its own. + + \item \textbf{Log flushing.} + A background thread that periodically flushes buffered log entries + to disk. If the application finishes, unsaved logs are + an acceptable loss compared with hanging the process. + + \item \textbf{Cache invalidation / expiry.} + A thread that sweeps an in-memory cache and removes stale entries + does not need to outlive the application. + + \item \textbf{Heartbeat / health-check threads.} + Service-mesh sidecars and microservices often send periodic + pings to a service-discovery system. + These should die with the service, not keep it alive. + + \item \textbf{IDE background indexing.} + IntelliJ IDEA's indexer and VS Code's language-server workers + are typically daemon-like: if you close the IDE, you do not + want to wait for them to finish indexing. +\end{enumerate} + +% ════════════════════════════════════════════════════════════════════════════ +\section{A Shorter Way to Create Threads – Lambda Expressions} +% ════════════════════════════════════════════════════════════════════════════ + +\begin{lstlisting}[caption={ThreadDemo.java}] +public class ThreadDemo { + public static void main(String[] args) { + Thread thread = new Thread(() -> { + System.out.println("Thread is running using a lambda!"); + }); + + thread.start(); + } +} +\end{lstlisting} + +\subsection{Expected output} + +\begin{lstlisting}[language={},numbers=none,backgroundcolor=\color{codebg}] +Thread is running using a lambda! +\end{lstlisting} + +\subsection{What is \code{() -> \{ \ldots \}} called?} + +The \code{() -> \{ \ldots \}} syntax is called a \textbf{lambda expression} +(also known as an \emph{anonymous function} or \emph{arrow function} in +other languages). + +A lambda expression is a concise way to provide an implementation of a +\emph{functional interface} – any interface that declares exactly one abstract +method. +\code{Runnable} is a functional interface: its single abstract method is +\code{void run()}. +The lambda \code{() -> \{ \ldots \}} supplies that implementation inline, +without naming it. + +\subsection{How does this differ from other approaches?} + +There are three common ways to supply a \code{Runnable} to a \code{Thread}: + +\begin{lstlisting}[caption={Three equivalent approaches}] +// 1. Class that extends Thread +class MyThread extends Thread { + public void run() { System.out.println("extends Thread"); } +} +new MyThread().start(); + +// 2. Named class that implements Runnable +class MyRunnable implements Runnable { + public void run() { System.out.println("implements Runnable"); } +} +new Thread(new MyRunnable()).start(); + +// 3. Lambda expression (shortest) +new Thread(() -> System.out.println("lambda")).start(); +\end{lstlisting} + +\begin{center} +\begin{tabular}{>{\bfseries}lllll} +\toprule +Approach & Boilerplate & Separate class? & Captures outer vars? & Extensible? \\ +\midrule +\code{extends Thread} & High & Yes (named) & No & Inherits Thread \\ +\code{implements Runnable} & Medium & Yes (named) & No & Yes \\ +Anonymous class & Medium & Yes (anon.) & Yes (eff.\ final) & Yes \\ +Lambda & \textbf{Minimal} & No & Yes (eff.\ final) & No \\ +\bottomrule +\end{tabular} +\end{center} + +\textbf{Key advantages of lambdas:} +\begin{itemize}[leftmargin=2em] + \item \textbf{Conciseness.} + No need to declare a class, override a method, or write a constructor. + A single line can create and start a thread. + + \item \textbf{Readability.} + The intent (``run this block of code in a new thread'') is immediately + obvious to the reader. + + \item \textbf{Variable capture.} + A lambda can reference local variables from the enclosing scope as long + as they are effectively final (i.e., never reassigned after declaration). + This is often more convenient than passing data through a constructor. + + \item \textbf{Functional programming patterns.} + Lambdas work seamlessly with the Java Streams API, \code{CompletableFuture}, + \code{ExecutorService}, and other modern concurrency utilities that accept + \code{Runnable} or \code{Callable} arguments. +\end{itemize} + +\textbf{Limitation:} +Because a lambda is not a subclass of \code{Thread}, it cannot override thread +life-cycle methods such as \code{interrupt()} or \code{setUncaughtExceptionHandler()} +directly inside the lambda body. +When that level of control is needed, a named class is more appropriate. + +% ── Footer note ────────────────────────────────────────────────────────────── +\vfill +\begin{center} + \small\color{gray} + Advanced Programming – Assignment 8 \quad|\quad + Faraz Ardeh \quad|\quad + Shahid Beheshti University \quad|\quad + June 2026 +\end{center} + +\end{document} diff --git a/src/main/java/DownloadWorker.java b/src/main/java/DownloadWorker.java index 546de2d..201330b 100644 --- a/src/main/java/DownloadWorker.java +++ b/src/main/java/DownloadWorker.java @@ -2,10 +2,7 @@ import java.util.Random; /** * Simulates downloading a single chunk of a file. - * - *
This class is intentionally provided as a skeleton for students. - * The main multithreading and simulation logic should be completed - * in the run() method.
+ * Each worker writes only to its own assigned ChunkStatus object. */ public class DownloadWorker implements Runnable { @@ -21,23 +18,33 @@ public class DownloadWorker implements Runnable { @Override public void run() { - // TODO: Record the chunk start time in chunkStatus. - double downloaded = 0.0; + // Record the start time for this chunk + chunkStatus.setStartTimeMs(System.currentTimeMillis()); - // TODO: Print a message that this chunk has started downloading. + double downloaded = 0.0; + int delayRange = config.getMaxStepDelayMs() - config.getMinStepDelayMs(); + double stepRange = config.getMaxStepDownloadMB() - config.getMinStepDownloadMB(); while (downloaded < chunkStatus.getChunkSizeMB()) { - // TODO: Generate a random sleep delay between min and max delay. - // TODO: Sleep for that delay. - // TODO: Generate a random download amount for this step. - // TODO: Increase downloaded, but do not go beyond chunk size. - // TODO: Save the updated downloaded value into chunkStatus. - // TODO: Optionally print step-by-step progress. + // Random delay simulating network latency for this step + int delay = config.getMinStepDelayMs() + (delayRange > 0 ? random.nextInt(delayRange) : 0); + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + + // Random amount downloaded in this step, capped at remaining size + double step = config.getMinStepDownloadMB() + random.nextDouble() * stepRange; + downloaded = Math.min(downloaded + step, chunkStatus.getChunkSizeMB()); + + // Write only to this worker's own ChunkStatus (thread-safe by design) + chunkStatus.setDownloadedMB(downloaded); } - // TODO: Mark the chunk as completed. - // TODO: Record the chunk end time in chunkStatus. - // TODO: Print a message that this chunk has finished downloading. + // Mark completion and record end time + chunkStatus.setEndTimeMs(System.currentTimeMillis()); + chunkStatus.setCompleted(true); } - } diff --git a/src/main/java/Main.java b/src/main/java/Main.java index 82bd239..decd7b3 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -3,7 +3,7 @@ import java.util.List; public class Main { public static void main(String[] args) { - System.out.println("=== Simulated Download Manager ==="); + System.out.println("=== Simulated Download Manager ===\n"); // 1. Read config DownloadConfig config; @@ -14,94 +14,98 @@ public class Main { return; } - System.out.println("File name: " + config.getFileName()); - System.out.println("Total size (MB): " + config.getTotalSizeMB()); - System.out.println("Chunk count: " + config.getChunkCount()); + System.out.println("File name : " + config.getFileName()); + System.out.println("Total size : " + config.getTotalSizeMB() + " MB"); + System.out.println("Chunks : " + config.getChunkCount()); + System.out.println("Step delay : " + config.getMinStepDelayMs() + + "–" + config.getMaxStepDelayMs() + " ms"); + System.out.println("Step size : " + config.getMinStepDownloadMB() + + "–" + config.getMaxStepDownloadMB() + " MB"); System.out.println(); - // 2. Create chunks + // ── MULTITHREADED RUN ────────────────────────────────────────────── + + System.out.println("━━━ Multithreaded Download ━━━"); + + // 2. Create chunk status objects List