DOC PREVIEW
UT CS 361 - Synchronization in Java

This preview shows page 1-2 out of 6 pages.

Save
View full document
View full document
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience
View full document
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience
Premium Document
Do you want full access? Go Premium and unlock all 6 pages.
Access to all documents
Download any document
Ad free experience

Unformatted text preview:

Synchronization in Java Ronald L. Rockhold 1/19/2006 Introduction Java’s synchronization model is based on another model, the Monitor. Like Hoare’s (and Hansen’s) Monitor model, Java’s synchronization is used to protect shared resources from simultaneous access/manipulation in a multi-threaded environment. Synchronization has costs. One component of which is the overhead of the synchronization implementation, e.g. acquiring/releasing a lock. Another, less obvious one, is the level of interference between multiple computations that do not require mutual exclusion, but manage to prevent each other from otherwise executing some code “simultaneously”. If two computations can safely take place simultaneously, but a mechanism for providing exclusion prevents this overlap, performance can be affected. If, for example, a single shared lock is used to control access to all critical sections, even safe combinations, then throughput can suffer. For this reason, critical sections are typically (logically) correlated with the data that each manipulates, and a lock is associated with that data. The critical section is then protected by a protocol (an agreement) requiring that the specific lock related to the specific instance of data needing protected access is first acquired. Protecting Instance Variables If a section of code must modify the internal fields of a specific object of type T, then class T can be “given” a lock as an instance variable, and the critical sections operating on instances t1 of type T, first acquire t1.lock. This allows different instances of T to be manipulated simultaneously by multiple threads, even by the same code. /* Some C/C++ code */ struct T { /* data fields, including: */ int count; struct Lock t_lock; }; void foo(struct T * t) { lock_acquire(t->t_lock); /* critical section entry */ count += 2; /* critcal section */ lock_release(t->t_lock); /* critical section exit */ } Since each instance of T has a different Lock, function foo() can be executing simultaneously in its critical section provided its argument t is pointing to differentinstances of T. But for those threads calling foo() with the same T instance, execution of the critical section will be serialized. It is critical to note that acquiring lock t_lock for some instance of T in no way protects the contents of that instance. For example: void spam(struct T* t) { t->count += 4; } Here, the spam() function will modify count for whatever instance it’s passed, even if some other thread has acquired the lock t->m_lock. So, critical sections are protected by a protocol, an “agreement” between programmers, that specifies a process that, if everyone follows, will ensure the coherence of shared data. For instances of T, the protocol is that critical section entry requires acquiring lock m_lock; exit consists of releasing m_lock. Note that we could have achieved safety in our critical sections by using a single lock shared by all the T instances. However, this would not have allowed different T instances to be accessed simultaneously, i.e. lock granularity would be too coarse. Synchronized Blocks The approach of defining a synchronization variable for each instance of a type is so common in multi-threaded environments, that Java does this automatically. In Java, every object (remember that an object is an instance of some class or is an array) has a (unique) monitor associated with it. We’ll talk more about Java’s monitors, but for now, think of a monitor as a lock (with a condition variable). Here’s the same code from above, this time using Java: class T { /* data fields, including: */ int count = 0; // Not needed: struct Lock t_lock; void foo() { /* instance method – i.e. not static */ synchronized(this) { /* critical section entry */ count += 2; /* critical section */ } /* critical section exit */ } } The Java synchronized statement is of the general form: synchronized(object_handle) { /* synchronized block */ }The synchronized block is not executed until the monitor associated with object_handle is acquired by the currently running Thread (only one Thread at a time can “own” the monitor). When the synchronized block is exited (e.g. return, falling off the end, un-caught exception), then the monitor is automatically released. So Java synchronization is block-based; the monitor is acquired before the block is entered, and it’s released (always and only – there is no “un-synchronized” keyword) when the synchronized block is exited. Unlike the lock model, there is no explicit mechanism for releasing a monitor. Like the lock model, there is nothing about acquiring an object’s monitor that in any way (other than programming protocol) protects the instance data of the object. If we add this spam() method to T : void spam() { count += 4; } Then our class instance methods are no longer thread-safe. If some other Thread “owns” the monitor for some instance t1 of T , and another Thread invokes t1.spam(), both Threads can be modifying t1 simultaneously. Synchronized Instance Methods Using the synchronized(this) block is so common in instance methods that Java provides a shortened notation for specifying that an instance method is synchronized against its invoking object. These are functionally equivalent definitions of instance method foo: synchronized void foo() { void foo() { synchronized(this) { count += 2; count += 2; } } } When a synchronized block is exited, another Thread can enter the monitor that’s now “free”. If there had been Threads waiting for the monitor, exactly one is allowed in. Java does not specify an ordering, so the Thread waiting the longest may not be the one allowed into the monitor. Protecting Class Variables So far our examples have focused on protecting access to per-object data. Sometimes there is data maintained per-class, rather than per-instance, that must be protected. In Java, per-class data are static members (“class variables”), and per-instance data are non-static (“instance variables”).Protecting class variables requires a protocol that ensures all critical sections synchronize against the same object. It wouldn’t work if one critical section used one object instance, and another critical section synchronized against a different object instance. What’s needed is an object


View Full Document

UT CS 361 - Synchronization in Java

Documents in this Course
Load more
Download Synchronization in Java
Our administrator received your request to download this document. We will send you the file to your email shortly.
Loading Unlocking...
Login

Join to view Synchronization in Java and access 3M+ class-specific study document.

or
We will never post anything without your permission.
Don't have an account?
Sign Up

Join to view Synchronization in Java 2 2 and access 3M+ class-specific study document.

or

By creating an account you agree to our Privacy Policy and Terms Of Use

Already a member?