Unit 6: Concurrency, Localization, and Multithreading
I. Foundations and Governing Principles
Concurrency allows multiple tasks to make progress during overlapping periods, while localization adapts an application’s language and regional behavior without changing its core logic. Java supports concurrency through threads, task abstractions, executors, and fork/join processing; it supports localization primarily through Locale and ResourceBundle.
- Concurrency: Structures independently executable tasks whose operations may be interleaved by the operating system and Java Virtual Machine (JVM).
- Parallelism: Executes tasks simultaneously on multiple processor cores; concurrency does not necessarily imply parallel execution.
- Thread safety: Requires shared mutable data to remain correct under arbitrary scheduling, commonly through synchronization, locks, atomic classes, or immutability.
- Task separation: Represents work with
RunnableorCallable, keeping the task definition separate from thread creation and management. - Executor framework: Manages thread creation, reuse, scheduling, shutdown, and result handling through high-level APIs.
- Localization principle: Separates locale-dependent resources, such as messages, from application code.
- Locale convention: Identifies linguistic and regional preferences through components such as language, country, and optional variant.
- Resource selection: Chooses the closest matching resource bundle using locale-specific fallback rules.
II. Scheduling and Concurrent Execution — Managing Independent Work
Java concurrency depends on scheduled threads, but applications should normally express work as tasks and delegate thread management to executor services.
A. Describing operating system task scheduling
Operating system scheduling determines which runnable thread receives processor time at a particular moment.
- Scheduler role: The operating system selects threads from the runnable set and assigns them to available CPU cores.
- Time slicing: On a preemptive system, a running thread may receive a short time quantum before another thread is scheduled.
- Context switch: Switching execution requires saving one thread’s state, including registers and program counter, and restoring another’s state.
- Thread priority: Java priorities range from
Thread.MIN_PRIORITY(1) toThread.MAX_PRIORITY(10), withNORM_PRIORITYequal to5.- Priority is a scheduling hint, not a guarantee of execution order.
- Behavior varies among operating systems and JVM implementations.
- Scheduling states: A thread waiting for I/O, a lock, or a timer cannot use the processor until its waiting condition ends.
- Nondeterminism: Two executions of the same program may interleave operations differently, so correctness must not depend on timing assumptions.
- Starvation risk: A thread can be delayed indefinitely when other threads repeatedly obtain the required processor time or shared resource.
B. Creating worker threads using Runnable and Callable
Runnable and Callable define units of work that can be executed by worker threads.
-
Runnable:- Method: Declares
void run()and cannot return a computed result. - Exceptions:
run()cannot declare checked exceptions. - Use case: Appropriate for actions such as logging, sending notifications, or updating independent records.
- Method: Declares
-
Callable<V>:- Method: Declares
V call() throws Exception, whereVis the result type. - Result access: Submission returns a
Future<V>whoseget()method waits for completion. - Failure access: If
call()fails,Future.get()throwsExecutionExceptionwrapping the original cause.
- Method: Declares
Runnable audit = () -> System.out.println("Audit complete");
Callable<Integer> total = () -> {
int a = 12;
int b = 8;
return a + b;
};- Worker principle: The task describes what must run; an executor controls where and when it runs.
- Cancellation:
future.cancel(true)requests interruption when the task is already running, but cooperative task code must respond to interruption.
C. Using an ExecutorService to concurrently execute tasks
ExecutorService executes submitted tasks through a managed pool of reusable worker threads.
- Creation:
Executors.newFixedThreadPool(2)creates a pool containing at most two active worker threads. - Submission:
execute(Runnable)starts work without producing aFuture, whereassubmit(...)returns aFuture. - Concurrency limit: With two workers and five tasks, no more than two tasks normally execute simultaneously; remaining tasks wait in the pool’s queue.
- Result retrieval:
Future.get()blocks until the result is available, whileisDone()provides a non-blocking completion check. - Order: Submission order does not guarantee completion order because task duration and scheduling differ.
- Lifecycle: Executor threads usually keep the JVM alive, so the service must be shut down.
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> result = pool.submit(() -> 40 + 2);
pool.execute(() -> System.out.println("Independent task"));
try {
System.out.println(result.get());
} finally {
pool.shutdown();
}- Shutdown behavior:
shutdown()rejects new tasks but permits submitted tasks to finish.shutdownNow()attempts to interrupt active tasks and returns tasks that never started.awaitTermination(...)waits for orderly termination for a bounded period.
D. RecursiveTask
RecursiveTask<V> represents a fork/join computation that recursively divides work and returns a result of type V.
- Framework: Tasks execute in a
ForkJoinPool, commonly using work-stealing so idle workers can take queued tasks from busy workers. - Required method: A subclass overrides
protected V compute(). - Base case: A sufficiently small input is processed directly to prevent unlimited subdivision.
- Recursive case: A large input is split into subtasks whose results are combined.
- Fork and join:
fork()schedules asynchronous execution;join()waits for and obtains the result.
class SumTask extends RecursiveTask<Long> {
private final long[] values;
private final int start, end;
SumTask(long[] values, int start, int end) {
this.values = values;
this.start = start;
this.end = end;
}
protected Long compute() {
if (end - start <= 1000) {
long sum = 0;
for (int i = start; i < end; i++) sum += values[i];
return sum;
}
int middle = (start + end) / 2;
SumTask left = new SumTask(values, start, middle);
left.fork();
long rightResult =
new SumTask(values, middle, end).compute();
return left.join() + rightResult;
}
}- Best fit: CPU-bound, divisible operations such as array aggregation are suitable; blocking network or database calls can reduce pool efficiency.
III. Multithreading — Thread Structure and Execution States
Multithreading places multiple threads of execution within one process, allowing responsive and concurrent programs while introducing coordination requirements.
A. Overview of multithreading
Multithreading enables a Java process to perform several activities through threads sharing the same heap.
- Shared process resources: Threads share objects, class data, and open process resources, but each thread has its own call stack and program counter.
- Responsiveness: A user-interface thread can remain responsive while a worker performs file or network operations.
- Throughput: Independent CPU-bound tasks may run in parallel when processor cores and suitable workers are available.
- Low isolation: An uncaught thread failure does not necessarily terminate every thread, but corrupted shared state can affect the whole process.
- Race condition: The result depends on interleaving, as when two threads perform a non-atomic
count++. - Visibility: Without a happens-before relationship established by mechanisms such as
synchronized,volatile, or thread start/join, one thread may not observe another’s latest writes. - Deadlock: Two threads can wait permanently when each holds one lock and requests the other.
B. Thread life cycle
A Java thread moves among states represented by the Thread.State enumeration.
NEW: TheThreadobject exists, butstart()has not been called.RUNNABLE: The thread is eligible to run or is currently executing; Java combines operating-system ready and running conditions in this state.BLOCKED: The thread is waiting to enter asynchronizedblock or method whose monitor another thread owns.WAITING: The thread waits indefinitely after operations such asObject.wait()without a timeout orThread.join().TIMED_WAITING: The thread waits for a bounded duration, as withThread.sleep(500)or timedjoin().TERMINATED: Therun()method has completed normally or ended because of an uncaught exception.- One-start rule: Calling
start()twice on the sameThreadproducesIllegalThreadStateException; create a new thread for another execution. - Interruption:
interrupt()requests that a thread stop waiting or notice cancellation; it does not forcibly terminate arbitrary code.
C. Creating tasks and threads
Creating concurrent work involves defining a task, associating it with a thread or executor, and starting execution.
- Task definition: A lambda can implement
Runnablebecause it is a functional interface with one abstract method. - Thread construction:
new Thread(task, "report-worker")associates the task with a named thread. - Starting execution:
start()creates a new execution path that invokesrun(). - Direct call distinction: Calling
run()directly executes on the current thread and creates no concurrency. - Completion coordination:
join()makes the calling thread wait until the target thread terminates.
Runnable task = () ->
System.out.println(Thread.currentThread().getName());
Thread worker = new Thread(task, "report-worker");
worker.start();
worker.join();- Preferred scale: Direct thread creation is reasonable for simple demonstrations or dedicated long-lived workers; executors are preferable for numerous short tasks.
D. Thread class and Runnable interface
Thread represents an execution mechanism, whereas Runnable represents work to be performed.
-
Extending
Thread:- Override
run()and invokestart()on the instance. - Java’s single inheritance rule prevents the class from extending another class.
- Task logic becomes tightly coupled to thread management.
- Override
-
Implementing
Runnable:- Implement
run()and pass the object toThreador an executor. - The same task type can be tested directly or executed through different concurrency mechanisms.
- The implementing class remains free to extend another class.
- Implement
class PrinterTask implements Runnable {
public void run() {
System.out.println("Printing");
}
}
Thread thread = new Thread(new PrinterTask());
thread.start();- Design preference:
Runnablegenerally provides better separation of concerns, reuse, and compatibility withExecutorService.
IV. Localization — Adapting Applications to Language and Region
Localization externalizes language- and region-dependent content so the same application can serve users with different conventions.
A. Describing the advantages of localizing an application
Localization improves accessibility and maintainability by adapting presentation without duplicating business logic.
- Language support: Labels such as
"Save"can be supplied as"Enregistrer"for French users through external resources. - Regional conventions: Dates, numbers, currencies, and percentages can follow locale-specific formats through classes such as
DateTimeFormatterandNumberFormat. - Maintainability: Translators can modify bundle values without changing Java control flow.
- Consistency: A shared message key, such as
menu.file, gives related screens a common translation source. - Scalability: Supporting an additional locale usually requires another resource bundle rather than a separate application build.
- Limit: Localization must account for text expansion, plural rules, character encoding, and right-to-left layouts; message translation alone is insufficient.
B. Reading and setting the locale using the Locale object
A Locale identifies language and regional preferences used by locale-sensitive Java APIs.
- Components: In
Locale.US, the language is"en"and the country is"US"; language codes are generally lowercase and country codes uppercase. - Reading defaults:
Locale.getDefault()obtains the JVM’s current default locale. - Setting defaults:
Locale.setDefault(locale)changes the process-wide default and can affect unrelated locale-sensitive operations. - Constants: Java supplies values such as
Locale.US,Locale.UK,Locale.FRANCE, andLocale.JAPAN. - Builder:
new Locale.Builder()validates components and supports language tags.
Locale current = Locale.getDefault();
Locale canadianFrench = new Locale.Builder()
.setLanguage("fr")
.setRegion("CA")
.build();
Locale.setDefault(canadianFrench);- Explicit preference: Passing a locale directly to formatting or bundle APIs avoids hidden dependence on the machine’s default settings.
C. Building a resource bundle for each locale
A resource bundle stores locale-specific key-value resources under a shared base name.
- Properties format: A base bundle named
Messages.propertiesmay containwelcome=Welcome. - Locale-specific file:
Messages_fr.propertiescan define the same key aswelcome=Bienvenue. - Regional specialization:
Messages_fr_CA.propertiescan override values specifically for Canadian French. - Key consistency: Every locale should use stable keys such as
button.submit; application code depends on keys, not translated values. - Java alternative: A bundle may extend
ListResourceBundleand return anObject[][], allowing values other than strings. - Fallback chain: For
fr_CA, Java searches progressively suitable bundles, including regional, language, and base candidates, subject to its bundle resolution rules. - Packaging: Bundle files must be available on the application classpath under the package corresponding to their base name.
D. Calling a resource bundle from an application
An application retrieves the appropriate bundle with ResourceBundle.getBundle() and accesses values by key.
- Lookup: The base name excludes locale suffixes and file extensions.
- Locale selection: Passing
Locale.FRANCErequests the closest matching French bundle. - Value retrieval:
getString("welcome")returns the text associated with that key. - Missing data: A missing bundle or unresolved key causes
MissingResourceException. - Formatting: Parameterized localized messages can be processed with
MessageFormatusing the same locale.
Locale locale = Locale.FRANCE;
ResourceBundle messages =
ResourceBundle.getBundle("i18n.Messages", locale);
String welcome = messages.getString("welcome");
System.out.println(welcome);- Runtime switching: Selecting another locale and loading the bundle again changes displayed resources without altering the application’s business logic.
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 →