Chapter 7: Concurrent Access to Objects and Variables

Chapter 7 - Concurrent Access to Objects and Variables

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Synchronization and the Collections API
The Collections API is new to JDK 1.2 and includes a number of interfaces and classes that facilitate the manipulation of collections of objects. Vector and Hashtable are now part of the Collections API, but have retained their old functionality for backward compatibility. Some of the new classes and interfaces include Collection , List , Map , Set , ArrayList , LinkedList , and HashMap .
Wrapping Collections to Make Them synchronized
Vector and Hashtable were originally designed to be multithread-safe. Take Vector , for examplethe methods used to add and remove elements are synchronized . If only one thread will ever interact with an instance of Vector , the work required to acquire and release the object-level lock is wasted .
The designers of the Collections API wanted to avoid the overhead of synchronization when it wasnt necessary. As a result, none of the methods that alter the contents of a collection are synchronized . If a Collection or Map will be accessed by multiple threads, it should be wrapped by a class that synchronizes all the methods.
  Caution Collections are not inherently multithread-safe. Extra steps must be taken when more than one thread will be interacting with a collection to make it multithread-safe.
There are several static methods in the Collections class that are used to wrap unsynchronized collections with synchronized methods:
public static Collection synchronizedCollection (Collection c)
public static List synchronizedList (List l)
public static Map synchronizedMap (Map m)
public static Set synchronizedSet (Set s)
public static SortedMap synchronizedSortedMap (SortedMap sm)
public static SortedSet synchronizedSortedSet (SortedSet ss)
Basically, these methods return new classes that have synchronized versions of the collections methods. To create a List that is multithread-safe and backed by an ArrayList , use the following:
List list = Collections.synchronizedList(new ArrayList());
Notice that the ArrayList instance was immediately wrapped and that no direct reference to the unsynchronized ArrayList exists. This is the safest approach. If another thread was to get a direct reference to the ArrayList instance, it could perform unsynchronized changes.
  Tip When synchronizing collections, do not keep any direct reference to the original unsynchronized collection. This will ensure that no other thread accidentally makes uncoordinated changes.
Safely Copying the Contents of a List into an Array
The SafeListCopy class in Listing 7.25 shows three different ways that the contents of a List can be safely copied into a String[] in a multithreaded environment.
Listing 7.25  SafeListCopy.javaSafely Copying the Contents of a List into an Array
1: import java.util.*;
2:
3: public class SafeListCopy extends Object {
4:     public static void printWords(String[] word) {
5:         System.out.println(word.length= + word.length);
6:         for (int i = 0; i < word.length; i++) {
7:             System.out.println(word[ + i + ]= + word[i]);
8:         }
9:     }
10:
11:     public static void main(String[] args) {
12:         // To be safe, only keep a reference to the
13:         // *synchronized* list so that you are sure that
14:         // all accesses are controlled.
15:         List wordList =
16:                 Collections.synchronizedList(new ArrayList());
17:
18:         wordList.add(Synchronization);
19:         wordList.add(is);
20:         wordList.add(important);
21:
22:         // First technique (favorite)
23:         String[] wordA =
24:                 (String[]) wordList.toArray(new String[0]);
25:
26:         printWords(wordA);
27:
28:         // Second technique
29:         String[] wordB;
30:         synchronized (wordList) {
31:             int size = wordList. size ();
32:             wordB = new String[size];
33:             wordList. toArray (wordB);
34:         }
35:
36:         printWords(wordB);
37:
38:         // Third technique (the ˜synchronized *is* necessary)
39:         String[] wordC;
40:         synchronized (wordList) {
41:             wordC = (String[]) wordList. toArray (
42:                                 new String[wordList. size ()]);
43:         }
44:
45:         printWords(wordC);
46:     }
47: }
In SafeListCopy , a multithread-safe version of List is constructed using Collections.synchronizedList() and is backed by an ArrayList instance (lines 1516). Three String s are added to the List (lines 1820).
The first technique for copying the contents of wordList into a String[] is the simplest and exploits a convenient feature of collections. The toArray() method is used to copy the contents into an array of the type specified by the parameter (lines 2324). Because the toArray() method was synchronized by wrapping, this is all that is necessary to safely copy the contents. If other threads were trying to add or remove elements during this operation, they would be blocked until it completed.
The second technique uses a synchronized statement to keep other threads out while two steps are performed (lines 2934). The first step is to determine the exact length needed for the destination String[] and to allocate the array (lines 3132). The second step is to pass this perfectly sized array to the toArray() method (line 33). This second technique has a slight efficiency advantage over the first one in that it does not create a throw-away, zero-length String[] .
The third technique combines the steps of the second technique into one line (lines 3943). It still needs to be inside a synchronized block because another thread could intervene after the size() method is called but before the toArray() method is called (line 42).
I would recommend that you stick with the first technique in most cases because it is the most straightforward and least error-prone . Listing 7.26 shows the output when SafeListCopy is run. Your output should match. Notice that in all three cases the proper set of strings is produced.
Listing 7.26  Output from SafeListCopy (Your Output Should Match)
word.length=3
word[0]=Synchronization
word[1]=is
word[2]=important
word.length=3
word[0]=Synchronization
word[1]=is
word[2]=important
word.length=3
word[0]=Synchronization
word[1]=is
word[2]=important
Safely Iterating Through the Elements of a Collection
The elements of a Collection can be stepped through one by one by using an Iterator .  In a multithreaded environment, you will generally want to block other threads from adding or removing elements while you are iterating through the current collection of elements.
The class SafeCollectionIteration shown in Listing 7.27 demonstrates how to block other threads while using an Iterator .
Listing 7.27  SafeCollectionIteration.javaSafely Iterating Through the Elements of a Collection
1: import java.util.*;
2:
3: public class SafeCollectionIteration extends Object {
4:     public static void main(String[] args) {
5:         // To be safe, only keep a reference to the
6:         // *synchronized* list so that you are sure
7:         // that all accesses are controlled.
8:
9:         // The collection *must* be synchronized
10:         // (a List in this case).
11:         List wordList =
12:                 Collections.synchronizedList(new ArrayList());
13:
14:         wordList.add(Iterators);
15:         wordList.add(require);
16:         wordList.add(special);
17:         wordList.add(handling);
18:
19:         // All of this must be in a synchronized block to
20:         // block other threads from modifying wordList while
21:         // the iteration is in progress.
22:         synchronized (wordList) {
23:             Iterator iter = wordList. iterator ();
24:             while (iter. hasNext ()) {
25:                 String s = (String) iter. next ();
26:                 System.out.println(found string: + s +
27:                     , length= + s.length());
28:             }
29:         }
30:     }
31: }
First, an ArrayList is wrapped in synchronization to ensure safe multithread access (lines 1112). Next, four strings are added to the List (lines 1417). A synchronized statement block is used to gain and hold the object-level lock for the List until the iteration is complete (lines 2229). When the lock is safely held, an Iterator is retrieved by invoking the iterator() method (line 23) of Collection ( List IS-A Collection ). The hasNext() and next() methods of Iterator are used to traverse through the elements (lines 2428). When the iteration is complete, the lock is released, and other threads (if there were any) are free to add and remove elements again.
Listing 7.28 shows the output produced when SafeCollectionIteration is run. Your output should match.
Listing 7.28  Output from SafeCollectionIteration (Your Output Should Match)
found string: Iterators, length=9
found string: require, length=7
found string: special, length=7
found string: handling, length=8

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net