...
Wiki Markup |
---|
Sometimes, a {{private static}} {{ThreadLocal}} variable is used per thread to maintain local state. When using thread pools, {{ThreadLocal}} variable should be used only if their lifetime is shorter than that of the corresponding task \[[Goetz 06|AA. Java References#Goetz 06]\]. Moreover, such variables should not be used as a communication mechanism between tasks. |
Noncompliant Code Example
Wiki Markup |
---|
This noncompliant code example (based on \[[Gafter 06|AA. Java References#Gafter 06]\]) shows a {{BrowserManager}} class that has several methods that use a fork-join mechanism, that is, they start threads and wait for them to finish. The methods are called in the sequence {{perUser()}}, {{perProfile}} and {{perTab()}}. The method {{methodInvoker}} spawns several instances of the specified runnable depending on the value of the variable {{numberOfTimes}}. A fixed sized thread pool is used to execute the enumerated threads generated at different levels. |
Code Block |
---|
|
public class BrowserManager {
private final ExecutorService pool = Executors.newFixedThreadPool(10);
int numberOfTimes;
private static volatile int count = 0;
public BrowserManager(int n) {
this.numberOfTimes = n;
}
public void perUser() {
methodInvoker(numberOfTimes, "perProfile");
pool.shutdown();
}
public void perProfile() {
methodInvoker(numberOfTimes, "perTab");
}
public void perTab() {
methodInvoker(numberOfTimes, "doSomething");
}
public void doSomething() {
System.out.println(++count);
}
public void methodInvoker(int n, final String method) {
final BrowserManager fm = this;
Runnable run = new Runnable() {
public void run() {
try {
Method meth = fm.getClass().getMethod(method);
meth.invoke(fm);
} catch (Throwable t) {
// Forward to exception reporter
}
}
};
Collection<Callable<Object>> c = new ArrayList<Callable<Object>>();
for(int i=0;i < n; i++) {
c.add(Executors.callable(run));
}
Collection<Future<Object>> futures = null;
try {
futures = pool.invokeAll(c);
} catch (InterruptedException e) {
// Forward to handler
}
// ...
}
public static void main(String[] args) {
BrowserManager manager = new BrowserManager(5);
manager.perUser();
}
}
|
Contrary to what is expected, this program does not print the total count, that is, the number of times doSomething()
is invoked. This is because it is susceptible to a thread starvation deadlock because the size of the thread pool (10) does not allow either thread from perTab()
to invoke the doSomething()
method. The output of the program varies for different values of numberOfTimes
and the thread pool size.
Compliant Solution
Risk Assessment
Executing interdependent tasks in a thread pool can lead to denial of service.
...
Wiki Markup |
---|
\[[API 06|AA. Java References#API 06]\]
\[[Gafter 06|AA. Java References#Gafter 06]\] [A Thread Pool Puzzler|http://gafter.blogspot.com/2006/11/thread-pool-puzzler.html] |
...
FIO36-J. Do not create multiple buffered wrappers on an InputStream 09. Input Output (FIO) 09. Input Output (FIO)