You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 163 Next »

Guidelines

CON00-J. Declare shared variables as volatile to ensure visibility and limit reordering of statements

CON01-J. Design APIs that ensure atomicity of composite operations and visibility of results

CON02-J. Always synchronize on the appropriate object

CON03-J. Do not use background threads during class initialization

CON04-J. Use the private lock object idiom instead of intrinsic synchronization

CON05-J. Ensure that threads do not fail during activation

CON06-J. Do not defer a thread that is holding a lock

CON07-J. Ensure atomicity of calls to thread-safe APIs

CON08-J. Do not call alien methods or constructors that synchronize on the same object as the calling class

CON09-J. Do not invoke alien methods that rely on invariants protected by the same lock object

CON10-J. Methods that override synchronized methods must also possess synchronization capabilities

CON11-J. Do not assume that declaring an object volatile guarantees visibility of its members

CON12-J. Avoid deadlock by requesting and releasing locks in the same order

CON13-J. Do not try to force thread shutdown

CON14-J. Do not let the "this" reference escape during object construction

CON15-J. Ensure actively held locks are released on exceptional conditions

CON16-J. Do not expect sleep() and yield() methods to have any synchronization semantics

CON17-J. Avoid using ThreadGroup APIs

CON18-J. Always invoke wait() and await() methods inside a loop

CON19-J. Use notifyAll() instead of notify() to resume waiting threads

CON20-J. Never apply a lock to methods making network calls

CON21-J. Facilitate thread reuse by using Thread Pools

CON22-J. Do not use incorrect forms of the double-checked locking idiom

CON23-J. Address the shortcomings of the Singleton design pattern

CON24-J. Use a unique channel to acquire locks on any file

CON25-J. Ensure atomicity when reading and writing 64-bit values

CON26-J. Do not publish partially-constructed objects

CON27-J. Do not execute classes that use ThreadLocal objects in a thread pool

Introduction

Memory that can be shared between threads is called shared memory or heap memory. The term variable is as used in this section refers to both fields and array elements [[JLS 05]]. Variables that are shared between threads are referred to as shared variables. All instance fields, static fields, and array elements are shared variables and are stored in heap memory. Local variables, formal method parameters, or exception handler parameters are never shared between threads and are not affected by the [memory model].

In a modern shared-memory multiprocessor architecture, each processor has one or more levels of cache that are periodically reconciled with main memory as shown in the following figure:

Because of this, the visibility of writes to shared variables can be problematic because the value of a shared variable may be cached and not written to main memory immediately. Consequently, another thread may read a stale value of the variable.

A further concern is that concurrent executions of code are typically interleaved and statements may be reordered by the compiler or runtime system to optimize performance. This results in execution orders that are not immediately obvious from an examination of the source code. Failure to account for possible reorderings is a common source of data races.

Consider the following example in which a and b are (shared) global variables or instance fields but r1 and r2 are local variables not accessible by other threads.

Initially, let a = 0 and b = 0.

Thread 1

Thread 2

a = 10;

b = 20;

r1 = b;

r2 = a;

Because, in Thread 1, the two assignments a = 10; and r1 = b; are not related, the compiler or runtime system is free to reorder them. Similarly in Thread 2, the statements may be freely reordered. Although it may seem counter-intuitive, the Java memory model allows a read to see a write that occurs later in the execution order.

A possible execution order showing actual assignments is:

Execution Order

Assignment

Assigned Value

Notes

1.

a = 10;

10

 

2.

b = 20;

20

 

3.

r1 = b;

0

Reads initial value of b, that is 0

4.

r2 = a;

0

Reads initial value of a, that is 0

In this ordering, r1 and r2 read the original values of the variables a and b even though they are expected to see the updated values, 10 and 20. Another possible execution order showing actual assignments is:

Execution Order

Statement

Assigned Value

Notes

1.

r1 = b;

20

Reads later value (in step 4.) of write, that is 20

2.

r2 = a;

10

Reads later value (in step 3.) of write, that is 10

3.

a = 10;

10

 

4.

b = 20;

20

 

In this ordering, r1 and r2 read the values of a and b written from step 3 and 4, even before the statements corresponding to these steps have executed.

Restricting the set of possible reorderings makes it easier to reason about the correctness of the code.

Even if statements execute in program order, caching can prevent the latest values from being reflected in the main memory (visibility hazard). Program order is the execution order that is expected when a single thread is running the statements sequentially, as written in a method.

The Java Language Specification defines the Java Memory Model (JMM) which provides certain guarantees to the Java programmer. The JMM is specified in terms of actions, which includes variable reads and writes, monitor locks and unlocks, and thread starts and joins. The JMM defines a partial ordering called happens-before on all actions within the program. To guarantee that a thread executing action B can see the results of action A, for example, there must be a happens-before relationship defined such that A happens-before B.

According to the JLS:

  1. An unlock on a monitor happens-before every subsequent lock on that monitor.
  2. A write to a volatile field happens-before every subsequent read of that field.
  3. A call to start() on a thread happens-before any actions in the started thread.
  4. All actions in a thread happen-before any other thread successfully returns from a join() on that thread.
  5. The default initialization of any object happens-before any other actions (other than default-writes) of a program.

If a happens-before relationship does not exist between two operations, the JVM is free to reorder them. A data race occurs when a variable is read by more than one thread, and written to by at least one thread, and the reads and writes are not ordered by a happens-before relationship. A correctly synchronized program is one with no data races.

Risk Assessment Summary

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

CON00-J

medium

probable

medium

P8

L2

CON01-J

medium

probable

medium

P8

L2

CON02-J

low

likely

high

P3

L3

CON03-J

low

probable

medium

P4

L3

CON04-J

low

probable

medium

P4

L3

CON05-J

low

probable

medium

P4

L3

CON06-J

low

probable

medium

P4

L3

CON07-J

low

likely

high

P3

L3

CON08-J

low

likely

high

P3

L3

CON09-J

low

probable

medium

P4

L3

CON10-J

low

probable

medium

P4

L3

CON11-J

low

likely

high

P3

L3

CON12-J

low

probable

medium

P4

L3

CON14-J

low

probable

medium

P4

L3

CON15-J

low

likely

low

P9

L2

CON16-J

low

probable

medium

P4

L3

CON17-J

low

probable

low

P6

L2

CON18-J

low

unlikely

medium

P2

L3

CON19-J

low

unlikely

medium

P2

L3

CON20-J

low

probable

high

P2

L3

CON21-J

low

probable

high

P2

L3

CON22-J

low

probable

medium

P4

L3

CON23-J

low

unlikely

medium

P2

L3

CON24-J

low

unlikely

medium

P2

L3

CON25-J

low

unlikely

medium

P2

L3


IDS17-J. Understand how escape characters are interpreted when String literals are compiled      The CERT Sun Microsystems Secure Coding Standard for Java      VOID CON00-J. Synchronize access to shared mutable variables

  • No labels