CSC/ECE 506 Spring 2011/ch10a dc

From Expertiza_Wiki
Revision as of 03:02, 5 April 2011 by Dmcarmon (talk | contribs) (First Draft)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Chapter 10a Supplement Java Memory Model

Introduction

This chapter supplement discusses the Java Memory Model (JMM). First of all, we will cover the motivation for a memory model for Java. Secondly, the main aspects of JMM including happens-before relationships and final and volatile variables. And lastly, we'll mention double-checking.

Motivation

With parallel computing becoming more popular, programming languages face many challenges including:

  • multiple layers of cache across different processors (this introduces problems with cache consistancy)
  • data races (that is, when two threads try to access the data at the same time when it is being altered by another thread)
  • synchronization directives. Note: these may be different depending on whether you have a "strong" or "weak" memory model (see chapter ?? of the Solihin ___ textbook)
  • compiler reordering of instructions

Programmers need to know how these issues effect program execution so that they can write good software for parallel systems. For example, C/C++ has the pthreads add-on to handle parallel programing. However, Java supports parallel programing by default. The JMM describes what is legal behavior for variables and memory accesses in programs with multiple threads.

Main Aspects

One of the most important parts of the Java Memory Model is the concept of "happens-before." Two statements have a happens-before relationship if one must come before the other.

Happens-before Illustration

For example:

// Thread 1: int xIsWritten = 0; x = 1; xIsWritten = 1;


// Thread 2: while(xIsWritten) {};

In this example it is important that the statement x = 1 is completed before xIsWritten = 1, and that this order is seen by both threads. In other words, x = 1 and xIsWritten need to have a happens-before relationship.

Happens-before Rules

Brian Goetz in Java Concurrency in Practice explains that happens-before relationships require the following rules:

  • Program order rule
  • Monitor lock rule
  • Volatile variable rule
  • Thread start rule
  • Thread termination rule
  • Interruption rule
  • Finalizer rule
  • Transitivity

Let's unpack this list. First of all, program order simply means that order that statements are written in should be the order that every processor sees them executed as, if they have a happens-after relationship. Secondly, with a monitor lock, the monitor is a "key" that a thread must get before it can access a synchronized block of code, and only one thread can have the "key" at any one time. This means that only one thread at a time is working the block of code. The monitor lock rule insures that ever lock and unlock is done in order. The volatile variable rule says that a volatile variable is like a monitor locked variable. The two thread rules insures that the thread doesn't start executing before the call to Thread.start() has finished, and before it is terminated every thread sees the result of its instructions. Similar to the thread start rule, the interruption rule says that a thread must complete calling an interrupt before the interrupt is started in the other thread. Also similar, the finalizer rule means that the constructor is completed before the finalizer starts. Lastly, transitivity means that if A->B and B->C, then A->C.

These rules are the main framework for the Java programming model.

Secondary Aspects

Beyond the main principles of the Java Memory Model there are many details.

Double-checked Locking

One of the controversial issues that came up with parallel computing and the JMM was double-checked locking. People wanted to avoid time consuming synchronization so they implemented double-checked locking. Here is an example of double-checked locking from Jeremy Manson and Brien Goetz (2004)

// From Manson and Goetz (2004) // double-checked-locking - don't do this!

private static Something instance = null;

public Something getInstance() {

 if (instance == null) {
   synchronized (this) {
     if (instance == null)
       instance = new Something();
   }
 }
 return instance;

}

The problem with this code is that the reads may be reordered by the compiler (they are not happens-after statements). This means the code produces indeterminate results which could lead you to attempt to read from a null pointer while thinking that it had been initialized to new Something(). This can be avoided in the new version of the JMM by declaring the instance volatile due to the volatile rule.

Summary