Advanced Java (Multithreading, Collections, JDBC)

Chapter 11
Concurrent Application Quality and Operations

Diploma in Programming Languages (Java & Python)
Advanced Java (Multithreading, Collections, JDBC)   •   Diploma

Concurrent Application Quality and Operations

Introduction and Context

A concurrent Java application can be functionally correct and still fail in production. It may deadlock only under unusual timing, exhaust a thread pool during a traffic spike, abandon work during shutdown, or show acceptable average latency while a small but important group of users experiences extreme delays. Consequently, concurrent programming requires more than creating threads correctly. It requires a disciplined operational lifecycle: testing, debugging, profiling, benchmarking, monitoring, cancellation, graceful shutdown, and capacity planning.

This chapter develops a practical quality and operations model for Java applications using Thread, executors, concurrent collections, futures, and asynchronous tasks. It also introduces thread dumps and the Java Flight Recorder (JFR)/Java Mission Control (JMC) toolchain. The emphasis is on diagnosing systems from evidence rather than guessing from source code or CPU percentage alone.

Learning Objectives

By the end of this chapter, a learner should be able to:

  • distinguish functional, safety, liveness, performance, and operational properties of concurrent software;
  • design tests for races, deadlocks, starvation, cancellation, and shutdown behavior;
  • interpret thread dumps and identify common thread states and contention patterns;
  • use JFR and JMC concepts to investigate CPU, allocation, locking, latency, and thread behavior;
  • construct meaningful benchmarks and avoid common measurement errors;
  • implement cancellation and graceful shutdown with deadlines;
  • select operational metrics and logs that support observability;
  • apply queueing and capacity-planning ideas to executor-based services.

1. Quality Dimensions in Concurrent Systems

Concurrent application quality has several dimensions.

Functional correctness means that outputs and externally visible behavior satisfy requirements. For example, an order should be charged once, not twice.

Safety means that undesirable events do not occur. Examples include data corruption, negative inventory, duplicate payment, and publication of partially initialized state.

Liveness means that desirable progress eventually occurs. A system that never completes a task may be safe but is not live.

Performance concerns response time, throughput, resource consumption, and scalability. Concurrency can increase throughput, but it can also introduce lock contention, context switching, queueing, and memory pressure.

Resilience concerns behavior during cancellation, failure, overload, interruption, dependency failure, and process termination.

Operability concerns whether engineers can understand and control the system in production. A concurrent system without useful metrics, logs, traces, and diagnostic procedures is difficult to maintain even when its source code appears simple.

These dimensions can conflict. Increasing a pool size may improve throughput for a short period but increase queueing and garbage collection later. Retrying a failed task may improve reliability but multiply load on an already overloaded dependency. Quality therefore requires explicit trade-offs and measurable acceptance criteria.

Important terminology

  • Race condition: Behavior depends on an uncontrolled timing relationship between concurrent actions.
  • Data race: Conflicting unsynchronized accesses to the same memory location, with at least one write.
  • Deadlock: Threads wait forever for resources held by one another.
  • Livelock: Threads remain active but repeatedly respond to one another without useful progress.
  • Starvation: A thread or task is indefinitely denied CPU time, a lock, or another required resource.
  • Contention: Multiple workers compete for a shared resource.
  • Backpressure: A mechanism that prevents producers from overwhelming consumers.
  • Tail latency: High-percentile response time, such as p95 or p99 latency.
  • Graceful shutdown: Stopping admission of new work while allowing accepted work to finish within a policy-defined limit.

2. Testing Concurrent Java Programs

2.1 Testing beyond ordinary unit tests

A sequential unit test may pass repeatedly while a race remains possible. Concurrent testing should vary scheduling, repetition, workload, and timing. Useful techniques include:

  1. Repeated execution: Run a suspicious scenario thousands of times. Repetition increases the chance of exposing a timing-dependent failure, although it does not prove correctness.
  2. Controlled coordination: Use CountDownLatch, CyclicBarrier, or Phaser to make several threads reach a critical point before proceeding. This is more reliable than inserting arbitrary sleeps.
  3. Stress testing: Exercise shared state under many operations, varied task sizes, and different pool sizes.
  4. Fault injection: Delay, fail, or interrupt dependencies; fill queues; reject tasks; and terminate workers at carefully selected points.
  5. Invariant checking: Assert properties such as “inventory never becomes negative” or “each request identifier is completed at most once.”
  6. Timeouts: Every test involving waiting should have a bounded timeout. An unbounded test can itself deadlock a build.

A sleep is not synchronization. Thread.sleep(10) merely makes a scheduling outcome more likely; it neither establishes a happens-before relationship nor guarantees that another thread has reached a desired state.

2.2 Safety and liveness test properties

Safety tests inspect results and state transitions. For a concurrent counter, a safety property might be that the final count equals the number of successful increments. Liveness tests inspect progress: tasks finish, shutdown terminates, and a blocked operation responds to interruption or cancellation.

Tests should also cover executor lifecycle states. A task submitted after shutdown() should be rejected. A task that ignores interruption should be recognized as a shutdown risk. A bounded queue should exhibit a documented policy when full: reject, block, drop, or execute in the caller.

2.3 Determinism and reproducibility

Concurrent failures are often difficult to reproduce. Record the random seed, Java version, operating-system details, processor count, pool configuration, and test parameters. Preserve thread dumps or event recordings when a failure occurs. Avoid making tests depend on the machine's incidental speed.

3. Debugging with Thread Dumps

A thread dump is a point-in-time report of threads, their states, stack traces, monitors, and often lock ownership. It is not a complete execution history. A single dump may show a transient condition, so several dumps taken a few seconds apart are usually more informative.

A dump can be obtained using tools such as jcmd <pid> Thread.print, depending on the Java version and deployment environment. Access permissions and production policies must be respected.

3.1 Interpreting common states

StateTypical meaningDiagnostic question
RUNNABLEExecuting or ready to execute; may also be in native codeIs it consuming CPU or blocked in a system call?
BLOCKEDWaiting to enter a synchronized monitorWhich thread owns the monitor?
WAITINGWaiting indefinitely, often through park() or wait()Is a signal, task, or lock release missing?
TIMED_WAITINGWaiting with a timeout, such as sleeping or timed parkingIs the timeout expected or masking a problem?
TERMINATEDFinished executionWas termination expected?

A RUNNABLE state does not guarantee that a thread is actively using a CPU. Correlate dumps with CPU profiles and operating-system metrics. A large group of executor workers in WAITING may be normal when the queue is empty.

3.2 Recognizing deadlock and starvation

A classic deadlock has a cycle: Thread A owns lock 1 and waits for lock 2, while Thread B owns lock 2 and waits for lock 1. The dump may explicitly report a Java-level deadlock. If no explicit report exists, compare “waiting to lock” and “locked” entries across threads.

Starvation is less obvious. A worker may repeatedly wait for a lock held by a long-running task, or a single executor may be occupied by tasks that synchronously wait for subtasks submitted to that same executor. This can produce thread starvation deadlock even when no monitor cycle exists.

A useful diagnostic sequence is:

  1. Identify the symptom: high latency, no progress, rejected tasks, or CPU saturation.
  2. Take at least three dumps at regular intervals.
  3. Compare stable stack frames and lock relationships.
  4. Check executor queue sizes and active counts.
  5. Correlate with CPU, garbage collection, database-pool, and external-service metrics.
  6. Form a hypothesis and reproduce it under a controlled test.

4. Profiling with JFR and JMC

Java Flight Recorder is a low-overhead event-recording facility in the JDK. It records selected runtime and application events, such as CPU samples, thread parks, monitor waits, allocation, garbage collection, exceptions, file operations, socket operations, and safepoint activity. Java Mission Control provides interfaces for examining recordings and identifying patterns.

JFR is most useful when the question is specific. Examples include:

  • Which methods consume CPU during the latency spike?
  • Which locks have the greatest total or longest wait time?
  • Are allocations causing frequent garbage collection?
  • Are tasks delayed in queues or blocked on dependencies?
  • Are virtual or platform threads spending time parked or waiting?

A recording should cover the incident or a representative workload. Configure event thresholds thoughtfully: recording every tiny event may generate noise, while thresholds that are too high may hide short but important waits.

4.1 CPU, lock, and allocation evidence

CPU samples show where threads spend execution time, not necessarily where wall-clock latency is spent. A service with low CPU may still have poor latency because threads wait on a database or lock. Lock events reveal contention, but a lock with many short waits may be less damaging than one with fewer, very long waits. Allocation events help distinguish application-level contention from garbage-collection pressure.

Use JFR together with thread dumps. A dump provides a snapshot of relationships; JFR provides a time-oriented view. Neither alone proves the root cause. Instrument important application boundaries with meaningful event names or metrics, while avoiding sensitive data in recordings.

5. Benchmarking Concurrent Code

Benchmarking asks a quantitative question under specified conditions. It is not the same as running a program once and reading elapsed time.

A sound benchmark defines:

  • workload and input distribution;
  • warm-up period;
  • measurement duration or iteration count;
  • thread and pool configuration;
  • JVM version, hardware, and operating-system conditions;
  • whether the result measures throughput, latency, allocation, or a combination;
  • statistical summary, especially percentiles for latency.

For Java microbenchmarks, the Java Microbenchmark Harness (JMH) addresses common problems such as dead-code elimination, inadequate warm-up, and compiler optimizations. A benchmark should consume returned results or otherwise ensure that useful work cannot be optimized away.

5.1 Throughput and latency

Throughput is completed work per unit time. If N operations complete in elapsed time T, then:

X = N / T

where X is throughput, measured for example in operations per second, N is the number of completed operations, and T is elapsed time in seconds. This calculation assumes that the measurement interval and completed-operation count are defined consistently.

Mean latency can conceal outliers. Report p50, p95, and p99 where user experience or service-level objectives require tail analysis. For example, p99 = 400 ms means 99% of measured requests are at or below 400 ms; it does not mean that every request takes 400 ms.

5.2 Avoiding misleading comparisons

Compare equivalent correctness guarantees. A lock-free counter that loses updates is not a faster implementation of a correct counter. Include synchronization, serialization, result validation, and cleanup in the measured scenario when they are part of real behavior. Test multiple concurrency levels: one thread, moderate concurrency, and overload. Performance that improves up to eight threads but collapses at 64 may indicate a scalability limit rather than a successful design.

6. Cancellation and Graceful Shutdown

Cancellation is cooperative in Java. Calling Future.cancel(true) requests cancellation and may interrupt the executing thread; it does not forcibly stop arbitrary code. A task must respond by observing interruption, using interruptible blocking operations, or checking a cancellation flag.

A robust task should preserve the interrupt status when it catches InterruptedException unless it is deliberately transferring the signal through another mechanism:

try {
    while (!Thread.currentThread().isInterrupted()) {
        Work item = queue.take();
        process(item);
    }
} catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
    // Perform bounded cleanup and return.
}

Catching InterruptedException and continuing silently converts a shutdown request into an apparent hang. Cleanup must also be bounded; otherwise, a task can prevent termination while attempting to release resources.

6.1 A shutdown protocol

A practical executor shutdown sequence is:

  1. Stop accepting new work.
  2. Signal cancellation where policy allows it.
  3. Call shutdown() to allow submitted tasks to finish.
  4. Wait for a defined deadline using awaitTermination.
  5. If the deadline expires, call shutdownNow() and record the returned tasks.
  6. Wait again for a bounded interval.
  7. Report or escalate tasks that remain active.
  8. Close resources in dependency order.

shutdownNow() is also cooperative: it interrupts workers and returns tasks that never started. It cannot guarantee that running tasks stop. External resources such as sockets, database calls, and blocking native operations may need their own timeouts or explicit closure.

6.2 Deadlines and budgets

A shutdown deadline should be propagated rather than repeatedly allocating a full timeout at each layer. If the overall deadline is time D and the current time is t, the remaining budget is D − t. Every child operation should use no more than that remaining budget. This prevents nested timeouts from exceeding the policy limit.

7. Observability for Concurrent Applications

Observability combines metrics, logs, traces, and diagnostic artifacts.

Metrics

Useful executor metrics include active worker count, pool size, queue depth, task submission rate, completion rate, rejection count, task age, and task execution duration. Also monitor lock-wait time, database connection-pool usage, garbage-collection pauses, CPU utilization, memory, and downstream latency.

Use stable metric names and bounded label cardinality. A request identifier may belong in a trace or log, but using an unbounded identifier as a metric label can exhaust the monitoring system.

Logs

Logs should identify the operation, outcome, duration, cancellation status, retry count, and relevant correlation identifier. Do not log passwords, tokens, payment data, or unrestricted personal information. Logging every task at high volume can itself become a performance bottleneck.

Traces

Distributed traces show where a request spends time across services. Trace spans should distinguish queue delay, execution time, lock or dependency wait, and retries where possible. A high end-to-end latency may be caused by a task waiting in an executor before its method begins.

Health and diagnostic endpoints

Readiness should indicate whether the instance should receive new work; liveness should indicate whether the process is functioning sufficiently to remain running. A liveness check that fails merely because a downstream service is slow can cause unnecessary restart loops. Diagnostic endpoints must be access-controlled because thread dumps and JFR recordings may disclose internal data.

8. Capacity Planning and Performance Models

Capacity planning connects expected demand to available service capacity. Little’s Law is particularly useful for a stable system over a sufficiently long observation interval:

L = λW

Here, L is the average number of items in the system, measured in tasks or requests; λ is the average arrival or throughput rate, measured in tasks per second; and W is the average time an item spends in the system, measured in seconds. Units are consistent because tasks = (tasks/second) × seconds. The law applies to a stable flow where items are neither accumulating indefinitely nor disappearing from the accounting boundary.

If a service processes 120 requests per second and the average end-to-end time is 0.25 seconds, the average number of requests in the system is:

L = 120 × 0.25 = 30 requests

This includes requests executing and waiting, if the measurement boundary includes both.

For a single-server abstraction, utilization is:

ρ = λS

where ρ is utilization, λ is arrival rate, and S is average service time per item. The relationship assumes one equivalent server and consistent units. For c approximately identical independent workers, a simple approximation is:

ρ = λS / c

This is a planning approximation, not a complete queueing model. Real systems have variable service times, blocking, dependencies, scheduling overhead, and finite queues. As utilization approaches 1, waiting time can rise sharply, so operating permanently at theoretical maximum utilization is unsafe.

Capacity planning should therefore include:

  • normal and peak arrival rates;
  • service-time distributions, not only averages;
  • dependency limits, such as database connections;
  • queue capacity and rejection policy;
  • CPU, memory, file descriptors, and network limits;
  • recovery and deployment behavior;
  • headroom for bursts and failure of a worker or instance.

A larger pool is not automatically better. If tasks are CPU-bound, excessive workers increase context switching. If tasks are blocking, more workers may help until a downstream limit is reached. Separate pools can prevent slow or untrusted work from starving latency-sensitive work, but they also increase configuration complexity.

9. Integrated Illustrative Case: Order Processing Service

This fictional case illustrates a production investigation.

Background

An order service receives HTTP requests, validates orders, reserves inventory, and publishes confirmation events. Validation is CPU-oriented; inventory calls use a database connection pool; event publication uses an external broker. The service uses a fixed executor and a bounded queue.

Problem

During a promotional event, p99 latency rises from 300 ms to 8 seconds. The average CPU utilization is only 45%, yet requests begin timing out. Operators also report slow shutdown during deployment.

Analysis

Metrics show that the executor queue grows while the database connection pool is fully leased. Thread dumps show many worker threads in timed waiting inside database acquisition, while request-handling threads wait for executor tasks. A JFR recording shows substantial time in database calls and lock contention around a local cache. The service has one executor for validation, database work, and event publication.

The evidence indicates that CPU is not the bottleneck. The shared executor allows blocked database tasks to occupy workers, delaying validation and publication. Queue growth increases end-to-end latency. During shutdown, database calls lack a sufficiently short timeout, and some tasks ignore interruption.

Approach and solution

The team applies the following changes:

  1. Separate CPU validation from blocking database work.
  2. Bound the database-work queue and reject or defer work using a documented overload policy.
  3. Align the database executor with the database connection limit rather than the number of incoming requests.
  4. Add queue-delay, task-age, connection-pool, rejection, and p99 metrics.
  5. Add database deadlines and preserve interruption status.
  6. Implement staged shutdown with an overall deadline.
  7. Benchmark normal, peak, and overload workloads using JMH for local components and a controlled load test for the service.
  8. Add an invariant test ensuring that an order identifier cannot be confirmed twice.

Outcome

In a controlled load test, normal p99 latency returns below the service objective, queue growth becomes visible before failure, and overload produces explicit rejection rather than indefinite waiting. Shutdown completes within the deployment budget for ordinary failures. The service still cannot accept unlimited demand; the improvement comes from making limits visible and behavior intentional.

Learning points

  • Low CPU does not imply spare capacity when workers wait on dependencies.
  • Pool sizing must respect downstream resources.
  • Queue delay is part of latency and must be measured.
  • Cancellation requires cooperation from every blocking layer.
  • Graceful degradation is preferable to hidden unbounded waiting.

10. Challenges, Governance, and Contemporary Practice

Concurrent behavior is difficult to reproduce because schedules depend on hardware, operating-system decisions, compiler optimization, garbage collection, and external timing. Performance results may also be invalid if test data differs from production or if observability overhead changes execution.

Operational governance should define who may capture thread dumps or JFR recordings, how sensitive data is protected, how long artifacts are retained, and how production experiments are approved. Diagnostic data can contain class names, URLs, SQL fragments, identifiers, and business information.

Modern Java systems may use virtual threads for large numbers of blocking-style tasks. Virtual threads reduce the cost of representing blocked tasks, but they do not remove limits imposed by databases, remote services, CPU, or locks. Pinning, excessive synchronization, unbounded task submission, and downstream saturation remain concerns. Structured concurrency concepts also encourage treating related child tasks as a bounded unit with coordinated cancellation and failure handling; adoption should follow the Java version and supported APIs of the target environment.

AI-assisted coding can generate executor or cancellation code, but generated code requires review against Java memory semantics, interruption behavior, resource ownership, security, and load characteristics. Automated suggestions do not replace thread dumps, measurements, or failure testing.

Best-Practice Checklist

  • Define correctness, liveness, latency, throughput, and shutdown objectives.
  • Use coordination primitives rather than sleep-based tests.
  • Bound queues and document overload behavior.
  • Separate workloads with materially different blocking or priority characteristics.
  • Capture multiple thread dumps during an incident.
  • Combine JFR, metrics, logs, traces, and dependency telemetry.
  • Benchmark warmed-up, representative, correctness-preserving workloads.
  • Report tail latency, not only averages.
  • Treat interruption as a cooperative cancellation protocol.
  • Propagate deadlines and bound every wait.
  • Monitor queue delay, task age, rejection, and downstream saturation.
  • Protect diagnostic artifacts and avoid sensitive data in telemetry.

Chapter Summary

Concurrent application quality includes functional correctness, safety, liveness, performance, resilience, and operability. Reliable testing uses coordination, repetition, invariants, bounded waits, and fault injection rather than arbitrary sleeps. Thread dumps reveal snapshots of states, stacks, and lock relationships; repeated dumps and correlation with system metrics improve diagnosis. JFR and JMC provide time-oriented evidence about CPU, allocation, locking, waits, and runtime behavior.

Meaningful benchmarks specify workload, warm-up, configuration, environment, and statistical measures. Throughput and latency must be interpreted together, with attention to percentiles and overload behavior. Java cancellation is cooperative, and graceful shutdown requires stopping admission, allowing bounded completion, interrupting when necessary, and reporting unfinished work. Observability depends on carefully designed metrics, logs, traces, and secure diagnostic procedures.

Capacity planning uses demand, service time, queueing, downstream limits, and headroom. Little’s Law relates average population, throughput, and time, while utilization approximations help identify saturation risk. The central operational principle is evidence-based control: make concurrency limits explicit, measure waiting as well as execution, and design failure and shutdown behavior as deliberately as the success path.

Key Terms

Thread dump: A point-in-time report of thread states, stacks, and lock information.

JFR: Java Flight Recorder, a runtime event-recording facility for diagnosing Java applications.

JMC: Java Mission Control, a tool for analyzing JFR recordings and JVM behavior.

Race condition: Timing-dependent behavior caused by uncontrolled concurrent interactions.

Deadlock: Permanent circular waiting among threads or resources.

Starvation: Indefinite denial of a required resource or scheduling opportunity.

Liveness: The property that useful progress eventually occurs.

Contention: Competition among concurrent activities for a shared resource.

Cancellation: A request that work should stop before normal completion.

Graceful shutdown: Bounded termination that stops new work and manages accepted work.

Backpressure: Limiting or slowing producers to protect consumers.

Throughput: Completed work per unit of time.

Latency: Time taken for an operation, often reported using percentiles.

Tail latency: High-percentile response time, such as p95 or p99.

Little’s Law: The stable-system relationship L = λW between population, throughput, and time.

Utilization: The fraction of available service capacity being used.

Capacity planning: Estimating resources and limits needed for expected demand.

Observability: The ability to infer internal behavior from outputs such as metrics, logs, traces, and recordings.

Backlog: Work that has arrived but has not yet completed.

Invariant: A condition that must remain true during and after concurrent execution.

Review Questions

Short Answer Questions

  1. Why is Thread.sleep() not a reliable synchronization mechanism?
  2. What information does a thread dump provide?
  3. Why should p99 latency be considered alongside average latency?
  4. What does Future.cancel(true) actually request?
  5. Name three metrics useful for monitoring an executor.

Long Answer Questions

  1. Explain how to investigate a suspected deadlock using multiple thread dumps and supporting metrics.
  2. Describe a complete graceful-shutdown protocol for an executor-based Java service.
  3. Compare the roles of JFR/JMC, thread dumps, metrics, logs, and distributed traces.
  4. Explain why increasing a thread-pool size can improve performance in one workload and harm it in another.
  5. Discuss how testing should address safety, liveness, cancellation, overload, and reproducibility.

Analytical / Application Questions

  1. A service processes 200 requests per second with an average system time of 0.15 seconds. Use Little’s Law to estimate the average number of requests in the system, and state what the measurement boundary must include.
  2. A worker pool has eight equivalent workers, each requiring an average of 40 ms of service per task. Using the simple utilization approximation, estimate utilization at 100 tasks per second and explain at least two reasons the real system may behave differently.
  3. During an incident, CPU is low, the executor queue is growing, and all database connections are occupied. Propose a diagnostic hypothesis, the evidence you would collect, and two corrective actions.

Multiple Choice Questions with Answers

Question 1

Which statement best describes Future.cancel(true)?

A. It forcibly terminates the task immediately.

B. It requests cancellation and may interrupt the executing thread.

C. It permanently shuts down the executor.

D. It guarantees that all dependent tasks are cancelled.

Correct Answer: B. It requests cancellation and may interrupt the executing thread.

Question 2

Which thread state commonly indicates that a thread is waiting to enter a synchronized monitor?

A. RUNNABLE

B. WAITING

C. BLOCKED

D. TERMINATED

Correct Answer: C. BLOCKED

Question 3

A service completes 120 requests per second and has an average system time of 0.25 seconds. Using Little’s Law, what is the average number of requests in the system?

A. 30

B. 48

C. 120

D. 480

Correct Answer: A. 30

Question 4

Which practice produces the most reliable concurrent test coordination?

A. Adding arbitrary sleeps

B. Assuming the fastest thread always runs first

C. Using latches or barriers to control milestones

D. Removing all timeouts

Correct Answer: C. Using latches or barriers to control milestones

Question 5

What is a primary strength of JFR compared with a single thread dump?

A. It forcibly fixes deadlocks.

B. It provides time-oriented runtime event data.

C. It guarantees zero recording overhead.

D. It replaces all application metrics.

Correct Answer: B. It provides time-oriented runtime event data.

Question 6

What should normally happen first in graceful executor shutdown?

A. Accept unlimited new tasks

B. Stop accepting new work

C. Delete the task queue without inspection

D. Restart every worker immediately

Correct Answer: B. Stop accepting new work

Question 7

A pool has eight workers, average service time is 0.04 seconds, and arrival rate is 100 tasks per second. Using ρ = λS/c, what is the approximate utilization?

A. 0.05

B. 0.32

C. 3.2

D. 32

Correct Answer: B. 0.32

Question 8

Why can low CPU utilization coexist with high request latency?

A. Waiting on locks or external dependencies consumes no substantial CPU.

B. Low CPU always proves the application is healthy.

C. Latency is unrelated to queues.

D. CPU utilization measures database connection availability.

Correct Answer: A. Waiting on locks or external dependencies consumes no substantial CPU.

Question 9

Which action best preserves cooperative interruption semantics after catching InterruptedException?

A. Ignore the exception and continue indefinitely.

B. Clear all thread-local data and restart the task.

C. Restore the interrupt status and perform bounded cleanup.

D. Call System.exit from the worker.

Correct Answer: C. Restore the interrupt status and perform bounded cleanup.

Question 10

Which metric most directly reveals work waiting before execution in a bounded executor?

A. Source-code line count

B. Queue depth or task age

C. Number of Java classes

D. Log file naming convention

Correct Answer: B. Queue depth or task age

Advanced Java (Multithreading, Collections, JDBC)
Chapter 11 of 15