Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: added another NCE, needs a CS

...

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
bgColor#FFCCCC

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

Code Block
bgColor#ccccff

TODO

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)