This question evaluates understanding of concurrent programming, thread-safety, semaphore-style resource management, exception-safe permit release, and extensible API design, and it falls under the Coding & Algorithms category testing concurrency/multithreading, synchronization primitives, and resource management.
Design and implement a thread-safe TaskExecutor that limits concurrent work using a semaphore-like mechanism.
You are given an interface similar to:
initialize(int maxThreads)
: sets up the executor with a maximum number of “worker permits”.
acquire(int permits)
: blocks until the requested number of permits is available, then reserves them.
execute(Task task, int input)
: runs a task while respecting the permit limit.
A Task is an interface and multiple subclasses implement different behaviors, but all must use the same interface. Each task declares:
requiredPermits()
), and
execute(...)
should block until the task finishes (blocking) or return immediately while the task continues in the background (fire-and-forget).
Example tasks:
MultiplyTask(factor, xPermits)
: multiplies
input
by
factor
, requires
xPermits
, and is fire-and-forget (i.e.,
execute
should not wait for completion).
DivideTask(divisor, yPermits)
: divides
input
by
divisor
, requires
yPermits
, and is blocking (i.e.,
execute
returns only after completion and returns the computed result).
Requirements:
maxThreads
.
Task
type should not require changing
TaskExecutor
logic beyond using the common
Task
interface.
Specify the key classes/interfaces and implement the core logic of initialize, acquire, and execute (language: Java or pseudocode acceptable).