A couple days ago, a co-worker and I stumbled upon an unintended constraint in some of the thread pool abstractions in the system we're working on. These threading abstractions leaned too heavily on the underlying ThreadPoolExecutor in Java for their semantics and did a poor job of hiding these from client code. Specifically, this class has corePoolSize and maximumPoolSize settings. The former is often misconstrued as a minimum pool size, which it's not; it's actually the maximum number of threads that should be created automatically before the work queue reaches its maximum size.
However, when this abstraction is well-known at a low level and then translated to a higher-level abstraction, that distinction may be lost in translation. When treated as a minimum size, it fails to meet expectations in a rather significant way. For instance, if you have the core size configured to be 1, the max size configured to be 20, and are using an unbounded work queue for the thread pool, the number of threads created to perform work will never exceed one!
There's adequate evidence that this behavior is confusing and somewhat non-intuitive, at least for some developers. I think having an explicit minimum setting might have helped alleviate this confusion, even if most developers would probably never have chosen something other than zero for it. The documentation explains how it functions, but developers sometimes assume they understand something when it looks intuitive. If there's a danger of misinterpreting something that looks otherwise intuitive, additional details in the interface that draw attention to these tripping points are valuable.
Also, this distinction was clearly known by those initially developing the components that interacts with this class, but as that abstraction was translated faithfully to a higher level (without encapsulation and introducing a separation of abstractions) it lead to unexpected behavior and confusion.
However, when this abstraction is well-known at a low level and then translated to a higher-level abstraction, that distinction may be lost in translation. When treated as a minimum size, it fails to meet expectations in a rather significant way. For instance, if you have the core size configured to be 1, the max size configured to be 20, and are using an unbounded work queue for the thread pool, the number of threads created to perform work will never exceed one!
There's adequate evidence that this behavior is confusing and somewhat non-intuitive, at least for some developers. I think having an explicit minimum setting might have helped alleviate this confusion, even if most developers would probably never have chosen something other than zero for it. The documentation explains how it functions, but developers sometimes assume they understand something when it looks intuitive. If there's a danger of misinterpreting something that looks otherwise intuitive, additional details in the interface that draw attention to these tripping points are valuable.
Also, this distinction was clearly known by those initially developing the components that interacts with this class, but as that abstraction was translated faithfully to a higher level (without encapsulation and introducing a separation of abstractions) it lead to unexpected behavior and confusion.
Comments
Post a Comment