Unit 6: Concurrency, Localization, and Multithreading - Subjective Questions
CSE406 — Advanced Java Programming • Practice Questions with Detailed Answers
20 questions
Explain how an operating system schedules tasks for execution. Discuss the role of processes, threads, CPU time slices, priorities, and context switching.
Operating system task scheduling is the mechanism used by an operating system to decide which process or thread should receive CPU time.
- A process is an independent program in execution, while a thread is a smaller execution unit within a process.
- The scheduler maintains a ready queue containing tasks that are prepared to run.
- In preemptive scheduling, the operating system can interrupt a running task and assign the CPU to another task.
- A time slice, or quantum, is the limited period for which a task may execute before the scheduler considers another task.
- Priority scheduling gives preference to tasks with higher priority, although excessive priority differences may cause starvation.
- During a context switch, the current task's registers, program counter, and execution state are saved, and another task's state is restored.
- Common scheduling approaches include first-come-first-served, round-robin, priority-based scheduling, and multilevel feedback queues.
Scheduling improves CPU utilization and responsiveness, but context switching introduces overhead. Java threads are ultimately scheduled by the operating system and JVM, so exact execution order should not be assumed.
Give an overview of multithreading in Java. Explain its benefits, limitations, and typical applications.
Multithreading is the execution of multiple threads within a single process. Each thread has its own execution path but shares the process's memory and resources.
Benefits:
- Better responsiveness in user interfaces and server applications.
- Improved CPU utilization, especially on multicore systems.
- Concurrent handling of independent tasks such as file access, network requests, and computation.
- Shared memory can make communication between threads efficient.
Limitations and risks:
- Threads that access shared mutable data can cause race conditions.
- Incorrect synchronization may lead to deadlock, starvation, or reduced performance.
- Thread creation and context switching consume system resources.
- Programs become more difficult to test and debug because execution order can vary.
Multithreading is commonly used in web servers, background services, database applications, games, simulations, and applications that perform input/output while continuing other work.
Describe the life cycle of a Java thread and explain the significance of the NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED states.
A Java thread moves through several states during its lifetime:
- NEW: The thread object has been created, but
start()has not yet been called. - RUNNABLE: After
start()is called, the thread is eligible to run. The JVM may be executing it or waiting for CPU time from the operating system. - BLOCKED: The thread is waiting to acquire a monitor lock required to enter a synchronized block or method.
- WAITING: The thread waits indefinitely for another thread to perform an action, such as notification or task completion. Methods such as
wait()andjoin()can cause this state. - TIMED_WAITING: The thread waits for a specified duration because of methods such as
sleep(), timedwait(), or timedjoin(). - TERMINATED: The
run()method has completed or ended because of an uncaught exception.
A thread cannot be restarted after it reaches the terminated state. Calling start() more than once on the same thread causes an IllegalThreadStateException.
Compare creating a task by extending the Thread class with creating a task by implementing the Runnable interface.
Both approaches can define code that executes concurrently, but implementing Runnable is generally more flexible.
Extending Thread:
- The class overrides the
run()method. - An object of the subclass represents both the task and the thread.
- The class cannot extend another class because Java supports single inheritance.
- It tightly couples the task logic to a particular thread object.
Implementing Runnable:
- The class implements
run()and is passed to aThreadconstructor. - The task remains separate from the mechanism used to execute it.
- The class can extend another class.
- The same task can be executed by different threads or submitted to an executor.
- Runnable is well suited to thread pools and modern concurrency APIs.
Example:
class PrintTask implements Runnable {
public void run() {
System.out.println("Running task");
}
}
Thread thread = new Thread(new PrintTask());
thread.start();Therefore, Runnable is preferred when the application needs reuse, separation of concerns, or executor-based execution.
Explain the steps for creating and starting a thread using the Thread class and the Runnable interface. Include a suitable Java example.
The general steps are:
- Define the work that should execute concurrently.
- Place the work in the
run()method of aThreadsubclass or aRunnableimplementation. - Create a thread object.
- Call
start(), which creates a new execution path and eventually invokesrun(). - Use
join()when the current thread must wait for completion.
Example using Runnable:
class DownloadTask implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println("Downloading part " + i);
}
}
}
public class ThreadExample {
public static void main(String[] args) throws InterruptedException {
Thread worker = new Thread(new DownloadTask());
worker.start();
worker.join();
System.out.println("Download completed");
}
}Calling run() directly does not create a new thread; it executes the method in the current thread. Calling start() is essential for concurrent execution.
Distinguish between a task and a thread in Java. Explain why separating task definition from task execution is useful.
A task is a unit of work, such as calculating a value or processing a file. A thread is an execution mechanism that runs a task.
- A task can be represented by
Runnable, which does not return a result, orCallable, which can return a result and throw checked exceptions. - A thread provides an independent flow of execution.
- One task may be executed by a newly created thread, a thread pool, or an executor service.
- Separating the two allows the application to change its execution policy without rewriting the business logic.
For example, a file-processing task can first be executed using a direct Thread, then later submitted to an ExecutorService without changing the task implementation. This separation improves reuse, testing, resource management, and scalability.
Explain the differences between Runnable and Callable in Java. Discuss their method signatures, return values, exception handling, and use with ExecutorService.
Runnable and Callable both represent units of work, but they support different contracts.
Runnable:
- Declares
void run(). - Does not return a result.
- Cannot directly throw checked exceptions.
- Is suitable for tasks whose primary purpose is an action.
Callable:
- Declares
V call(). - Returns a value of type
V. - Can throw checked exceptions.
- Is suitable for calculations or operations whose result must be collected.
Example:
Callable<Integer> task = () -> 20 + 22;
Future<Integer> result = executor.submit(task);
Integer value = result.get();When submitted to an executor, a Runnable may return a Future<?> mainly for completion and cancellation, while a Callable<V> returns a Future<V> containing the computed result. Future.get() may block until the task completes and may throw execution or interruption-related exceptions.
Describe the purpose and working of an ExecutorService. Explain how it improves upon manually creating a new thread for every task.
An ExecutorService is a high-level Java concurrency framework that manages task execution using one or more worker threads.
Working process:
- The application creates an executor with a fixed, cached, or scheduled thread pool.
- Tasks are submitted using
execute()orsubmit(). - The executor places tasks in a queue and assigns them to available worker threads.
- A submitted
Callableproduces aFuture, which can be used to obtain a result or cancel the task. - The executor should be shut down after use.
Example:
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(() -> processFile());
executor.shutdown();Compared with manual thread creation, an executor:
- Reuses threads and reduces creation overhead.
- Controls the maximum number of concurrent tasks.
- Queues excess work.
- Provides result handling, cancellation, and lifecycle management.
- Makes the application easier to scale and maintain.
The application should not submit new work after shutdown, and it should handle interruption correctly when awaiting termination.
Explain the difference between execute() and submit() in ExecutorService. Include the behavior of returned values and exception handling.
execute() and submit() both schedule tasks, but they provide different APIs.
execute(Runnable)accepts aRunnableand returns no value.submit(Runnable)accepts aRunnableand returns aFuture<?>.submit(Callable<T>)accepts aCallable<T>and returns aFuture<T>containing the eventual result.- A
Futuresupports operations such asget(),isDone(),isCancelled(), andcancel().
Exception behavior also differs. An exception from a task submitted with submit() is captured and becomes available through Future.get(), usually wrapped in an ExecutionException. With execute(), an uncaught exception is handled by the thread's uncaught-exception mechanism.
submit() is appropriate when the caller needs completion status, a return value, or controlled exception observation. execute() is suitable for fire-and-forget actions where no result is required.
Discuss the different ways to shut down an ExecutorService. Explain shutdown(), shutdownNow(), awaitTermination(), and the importance of handling InterruptedException.
An executor must be shut down so that its worker threads do not keep the application alive indefinitely.
shutdown()performs an orderly shutdown. Previously submitted tasks are allowed to complete, but new tasks are rejected.shutdownNow()attempts to stop actively executing tasks by interrupting their threads and returns tasks that were waiting in the queue. It cannot guarantee that a task will stop if the task ignores interruption.awaitTermination(timeout, unit)blocks the calling thread until termination, the timeout expires, or the thread is interrupted.
A common pattern is:
executor.shutdown();
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException ex) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}Restoring the interrupt status is important because it preserves the interruption request for higher-level code. Tasks should check interruption and release resources promptly.
What is RecursiveTask in Java? Explain how the fork/join framework uses it to solve recursive problems.
RecursiveTask<V> is an abstract class in the java.util.concurrent package used for fork/join computations that return a result of type V.
A typical algorithm follows these steps:
- Define a task that extends
RecursiveTask<V>. - Implement the
compute()method. - Check whether the problem is small enough for a direct solution.
- If it is large, divide it into subtasks.
- Use
fork()to schedule one or more subtasks. - Use
join()to wait for their results. - Combine the results.
The fork/join pool uses work stealing. An idle worker can take tasks from another worker's deque, improving processor utilization.
RecursiveTask is useful for divide-and-conquer operations such as parallel array processing, searching, sorting, and recursive numerical computations. It is different from RecursiveAction, which performs work but does not return a result.
Develop and explain a RecursiveTask implementation that calculates the sum of an integer array using divide-and-conquer parallelism.
A recursive sum task divides the array until each segment is small, computes small segments directly, and combines the partial sums.
class SumTask extends RecursiveTask<Long> {
private final int[] values;
private final int start;
private final int end;
private static final int THRESHOLD = 1000;
SumTask(int[] values, int start, int end) {
this.values = values;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if (end - start <= THRESHOLD) {
long total = 0;
for (int i = start; i < end; i++) total += values[i];
return total;
}
int middle = (start + end) / 2;
SumTask left = new SumTask(values, start, middle);
SumTask right = new SumTask(values, middle, end);
left.fork();
long rightResult = right.compute();
long leftResult = left.join();
return leftResult + rightResult;
}
}The threshold prevents excessive task creation. If the input has elements and the split is balanced, the recursion depth is approximately , while the total amount of addition remains . Parallel speedup depends on available processors, task overhead, and memory access.
Compare ExecutorService with the fork/join framework. State the type of problem for which each approach is most appropriate.
ExecutorService and fork/join both manage concurrent execution, but they target different workload patterns.
ExecutorService:
- Works well with independent or loosely related tasks.
- Supports
RunnableandCallable. - Provides fixed, cached, scheduled, and other executor configurations.
- Is suitable for server requests, background jobs, and I/O-bound operations.
- Usually uses a queue of submitted tasks.
Fork/join framework:
- Is designed for recursive divide-and-conquer algorithms.
- Uses
RecursiveTaskfor results andRecursiveActionfor no-result operations. - Uses work stealing to balance recursively created subtasks.
- Is particularly suitable for CPU-intensive operations that can be split into independent subtasks.
An executor is usually the clearer choice for a collection of unrelated jobs. Fork/join is preferable when a large problem can be repeatedly divided and partial results can be combined.
Explain the advantages of localizing an application. Discuss language, regional, cultural, and formatting considerations.
Localization adapts an application to a particular language and region without requiring a separate codebase for every market.
Important advantages include:
- Wider accessibility: Users can interact with the application in their preferred language.
- Improved usability: Dates, times, numbers, currencies, and messages follow familiar regional conventions.
- Cultural appropriateness: Text, symbols, sorting rules, and presentation can respect local expectations.
- Reduced maintenance: Translators can update resource data without changing program logic.
- Market expansion: A localized product can support users in multiple countries.
- Consistency: Shared localization mechanisms ensure that messages and formats are applied uniformly.
Localization should separate translatable content from source code. It should also account for text expansion, right-to-left scripts, character encodings, plural rules, time zones, and locale-specific currency and date conventions.
Describe the Locale class in Java. Explain how to read the default locale, create a specific locale, and set the default locale.
The Locale class represents a language, country, and optional variant or extension. It is used by Java APIs to select locale-sensitive resources and formatting rules.
Examples:
Locale current = Locale.getDefault();
Locale frenchCanada = Locale.CANADA_FRENCH;
Locale german = Locale.of("de", "DE");
Locale.setDefault(german);Locale.getDefault()reads the locale configured for the JVM or operating system.- Predefined constants such as
Locale.USandLocale.UKrepresent common locales. - A locale can be constructed from language and country codes. Language codes generally use ISO language codes, and country codes generally use ISO country codes.
Locale.setDefault(locale)changes the default locale used by many locale-sensitive APIs in the JVM.
Changing the global default should be done carefully because it can affect unrelated code. For predictable behavior, pass an explicit locale to formatters and resource lookup operations whenever possible.
Explain the difference between a language, a country, and a locale. Why is a language-only locale sometimes insufficient for application formatting?
A language identifies a human language, such as English or French. A country identifies a geographic region, such as the United States or Canada. A locale combines regional information used to select language and cultural conventions.
For example, English is used in both the United States and the United Kingdom, but formatting conventions differ:
- Date formats can differ, such as month-day-year versus day-month-year.
- Currency symbols and currency codes differ.
- Number separators can differ.
- Spelling and wording can differ.
Therefore, a language-only locale such as en may identify the language but cannot always determine the correct regional formatting. An application displaying currency, dates, addresses, or legal information should generally use a locale containing both language and country, such as en-US or en-GB.
Explain how to build a resource bundle for multiple locales. Include the naming convention and the role of the base bundle.
A resource bundle stores locale-dependent values separately from application logic. A common implementation uses .properties files.
For a base name of Messages, files may include:
Messages.propertiesfor default values.Messages_fr.propertiesfor French.Messages_de_DE.propertiesfor German in Germany.
The naming pattern is generally:
baseName_language_country_variantEach file contains key-value pairs:
properties
welcome=Welcome
items=Items
The localized file uses the same keys with translated values:
properties
welcome=Bienvenue
items=Articles
The base bundle acts as a fallback when a more specific locale bundle is unavailable or when a requested key is missing, depending on the resource-bundle lookup process. Keys should remain stable across files, and translators should not modify key names. Values may contain placeholders that the application formats separately.
Describe how an application loads and uses a resource bundle for a selected locale. Provide a Java example using ResourceBundle.
ResourceBundle loads locale-specific resources by using a base name and a Locale object.
Locale locale = Locale.of("fr", "FR");
ResourceBundle messages =
ResourceBundle.getBundle("Messages", locale);
String greeting = messages.getString("welcome");
System.out.println(greeting);The lookup process searches for the most specific matching bundle and then falls back to less specific bundles and the base bundle. The base name is normally a package-qualified name when the bundle is stored inside a package.
Important practices include:
- Keep identical keys in all locale files.
- Use
getString()for text values and convert other values explicitly when needed. - Handle
MissingResourceExceptionwhen a bundle or key is unavailable. - Keep resource files on the application's classpath.
- Use message formatting, such as
MessageFormat, for values containing parameters.
This approach lets the same Java code display different text according to the selected locale.
Explain resource-bundle fallback and discuss what happens when a requested locale or resource key is missing.
When ResourceBundle.getBundle() is called, Java searches for a bundle that best matches the requested locale. For a locale such as fr-CA, the search can consider a country-specific French bundle, a language-only French bundle, the default locale, and finally the base bundle, depending on the lookup rules and available resources.
If no suitable bundle can be found, Java throws MissingResourceException. If the bundle exists but getString() requests a key that is absent, the same exception type can be thrown for the missing key.
A robust application should:
- Provide a complete base bundle containing safe default text.
- Keep keys synchronized across translated files.
- Test every supported locale.
- Log missing translations during development.
- Avoid displaying raw keys to end users unless that is an intentional fallback.
Fallback improves resilience, but it can hide incomplete translations. It should therefore be combined with validation during testing or build time.
Design a localized Java application that displays a date, currency value, and message for a selected locale. Explain which Java classes should be used and why.
A localized application should pass the selected locale explicitly to each locale-sensitive operation.
- Use
Localeto represent the user's language and region. - Use
ResourceBundlefor translated labels and messages. - Use
DateTimeFormatterwith an explicit locale for dates and times. - Use
NumberFormat.getCurrencyInstance(locale)for currency values. - Use
MessageFormator a type-safe message strategy for parameterized messages.
Example:
Locale locale = Locale.US;
ResourceBundle bundle = ResourceBundle.getBundle("Messages", locale);
DateTimeFormatter dateFormat =
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG).withLocale(locale);
NumberFormat money = NumberFormat.getCurrencyInstance(locale);
String date = dateFormat.format(LocalDate.now());
String amount = money.format(1250.50);
String label = bundle.getString("total");Explicit locales prevent unexpected changes caused by a JVM-wide default locale. The design should also test regional differences, including decimal separators, currency symbols, date order, and translated text length.
Explain how an operating system schedules tasks for execution. Discuss the role of processes, threads, CPU time slices, priorities, and context switching.
Operating system task scheduling is the mechanism used by an operating system to decide which process or thread should receive CPU time.
- A process is an independent program in execution, while a thread is a smaller execution unit within a process.
- The scheduler maintains a ready queue containing tasks that are prepared to run.
- In preemptive scheduling, the operating system can interrupt a running task and assign the CPU to another task.
- A time slice, or quantum, is the limited period for which a task may execute before the scheduler considers another task.
- Priority scheduling gives preference to tasks with higher priority, although excessive priority differences may cause starvation.
- During a context switch, the current task's registers, program counter, and execution state are saved, and another task's state is restored.
- Common scheduling approaches include first-come-first-served, round-robin, priority-based scheduling, and multilevel feedback queues.
Scheduling improves CPU utilization and responsiveness, but context switching introduces overhead. Java threads are ultimately scheduled by the operating system and JVM, so exact execution order should not be assumed.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →