Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Wiki Markup
If the Java Virtual Machine (JVM) interacts with a file system that operates over an unreliable network, file I/O might incur a large performance penalty. In such cases, avoid file I/O over the network when holding a lock. File operations (such as logging) that may block waiting for the output stream lock or for I/O to complete may be performed in a dedicated thread to speed up task processing. Logging requests can be added to a queue given that the queue's {{put()}} operation incurs little overhead as compared to file I/O \[[Goetz 06, pg 244|AA. Java References#Goetz 06]\].

Noncompliant Code Example (

...

Deferring a

...

Thread)

This noncompliant code example defines a utility method that accepts a time parameter argument.

Code Block
bgColor#FFCCCC
public synchronized void doSomething(long time)
  throws InterruptedException {
  // ...
  Thread.sleep(time);
}

...

This compliant solution defines the doSomething() method with a timeout parameter instead of the time value. The use of the Using Object.wait() method instead of Thread.sleep() allows setting a time out for a period during which a notification may awaken the thread.

...

The current object's monitor is immediately released upon entering the wait state. After the time out period has elapsed, the thread attempts to reacquire resumes execution after reacquiring the current object's monitor and resumes execution when it succeeds.

Wiki Markup
According to the Java API class {{Object}} documentation \[[API 06|AA. Java References#API 06]\]:

...

Ensure that a thread that holds locks on other objects releases them appropriately, before entering the wait state. Also, refer to the related guidelines Additional guidance on waiting and notification in available in CON18-J. Always invoke wait() and await() methods inside a loop and CON19-J. Notify all waiting threads instead of a single thread.

...

Code Block
bgColor#FFcccc
// Class Page is defined separately. It stores and returns the Page name via getName()
Page[] pageBuff = new Page[MAX_PAGE_SIZE];

public synchronized boolean sendPage(Socket socket, String pageName) throws IOException {
  // Get the output stream to write the Page to
  ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());

  // Find the Page requested by the client (this operation requires synchronization)
  Page targetPage = null;
  for (Page p : pageBuff) {
    if (p.getName().compareTo(pageName) == 0) {
      targetPage = p;
    }
  }

  // Requested Page does not exist
  if (targetPage == null) {
    return false;
  } 

  // Send the Page to the client (does not require any synchronization)
  out.writeObject(targetPage);

  out.flush();
  out.close();
  return true;
}

...

Compliant Solution

This compliant solution entails separating separates the actions process into a sequence of steps:

  • Perform actions on data structures requiring synchronization
  • Create copies of the objects that are required to be sent
  • Perform network calls in a separate method that does not require any synchronization

In this compliant solution, the synchronized method getPage() is called from an unsynchronized method sendPage(), to find retrieve the requested Page in the pageBuff array. After the Page is retrieved, the method sendPage() calls the unsynchronized method deliverPage() to deliver the Page to the client.

Code Block
bgColor#ccccff
public boolean sendPage(Socket socket, String pageName) { // No synchronization
  Page targetPage = getPage(pageName); 

  if (targetPage == null)
    return false;

  return deliverPage(socket, targetPage);
}

private synchronized Page getPage(String pageName) { // Requires synchronization
  Page targetPage = null;

  for (Page p : pageBuff) {
    if (p.getName().equals(pageName)) {
      targetPage = p;
    }
  }
  return targetPage;
}

public boolean deliverPage(Socket socket, Page page){
  try{
    // Get the output stream to write the Page to
    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());

    // Send the Page to the client
    out.writeObject(page);

  } catch (IOException io){
    // If recovery is not possible return false
    return false;    
  } finally {
    out.flush();
    out.close();
  }  
  return true;
}

Exceptions

EX1: Classes that are compliant with the guideline provide an appropriate termination mechanism to callers are allowed to violate this guideline (see CON24-J. Ensure that threads and tasks performing blocking operations can be terminated in that they provide an appropriate termination mechanism to callers, are allowed to violate this guideline).

EX2: A method that requires multiple locks may hold several locks while waiting for the remaining locks to become available. This constitutes a valid exception, though care must be taken to avoid deadlock. See CON12-J. Avoid deadlock by requesting and releasing locks in the same order for more information.

Risk Assessment

If blocking Blocking or time consuming lengthy operations are performed within synchronized regions , temporary or permanent deadlock may result in a deadlocked or unresponsive system.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON20-J

low

probable

high

P2

L3

...