Friday, October 23, 2009

Avoiding Blocking on synchronized



















Chapter 17 - The BooleanLock Utility

Java Thread Programming
Paul
Hyde






size=1>  size=2>Copyright � 1999 Sams
Publishing




















size=4>Avoiding Blocking on synchronized












size=2>The size=2>synchronized
keyword can be used for methods and statement blocks to ensure that
only one thread is allowed access at a time. When threads are
blocked waiting to acquire the
face="Courier New" size=2>synchronized face=Arial size=2> lock, they do not respond to interrupts. In
addition, there is no way to limit the amount of time that a thread
will wait to enter a
size=2>synchronized
section. This can become a problem when the work done inside a
size=2>synchronized
section takes a relatively long time to complete. The examples in
this section demonstrate how to work around this
situation.












size=4>SyncBlock












face="Courier New" size=2>SyncBlock face=Arial size=2> (see Listing 17.4) demonstrates how threads
blocked on a
size=2>synchronized
statement ignore interrupt requests.












size=2>Listing 17.4  SyncBlock.java—Threads Blocked on
synchronized Ignore Interrupts












face="Courier New" size=2>1: public class SyncBlock extends Object
{










face="Courier New" size=2>2:     private Object
longLock;










face="Courier New" size=2>3:










face="Courier New" size=2>4:     public
SyncBlock() {










face="Courier New"
size=2>5:         longLock =
new Object();










face="Courier New" size=2>6:     }










face="Courier New" size=2>7:










face="Courier New" size=2>8:     public void
doStuff() {










face="Courier New"
size=2>9:        
print(“about to try to get exclusive access “ +










face="Courier New"
size=2>10:                
“to longLock”);










face="Courier New" size=2>11:










face="Courier New"
size=2>12:        
synchronized ( longLock ) {










face="Courier New"
size=2>13:            
print(“got exclusive access to longLock”);










face="Courier New"
size=2>14:            
try { Thread.sleep(10000); }










face="Courier New"
size=2>15:            
catch ( InterruptedException x ) { }










face="Courier New"
size=2>16:            
print(“about to relinquish exclusive access to “ +










face="Courier New"
size=2>17:                    
“longLock”);










face="Courier New"
size=2>18:        
}










face="Courier New" size=2>19:     }










face="Courier New" size=2>20:










face="Courier New" size=2>21:     private static
void print(String msg) {










face="Courier New"
size=2>22:         String
name = Thread.currentThread().getName();










face="Courier New"
size=2>23:        
System.err.println(name + “: “ + msg);










face="Courier New" size=2>24:     }










face="Courier New" size=2>25:










face="Courier New" size=2>26:     private static
Thread launch(










face="Courier New"
size=2>27:                
final SyncBlock sb,










face="Courier New"
size=2>28:                
String name










face="Courier New"
size=2>29:            
) {










face="Courier New" size=2>30:










face="Courier New"
size=2>31:         Runnable
r = new Runnable() {










face="Courier New"
size=2>32:                
public void run() {










face="Courier New"
size=2>33:                    
print(“in run()”);










face="Courier New"
size=2>34:                    
sb.doStuff();










face="Courier New"
size=2>35:                
}










face="Courier New"
size=2>36:            
};










face="Courier New" size=2>37:










face="Courier New"
size=2>38:         Thread t
= new Thread(r, name);










face="Courier New"
size=2>39:        
t.start();










face="Courier New" size=2>40:










face="Courier New"
size=2>41:         return
t;










face="Courier New" size=2>42:     }










face="Courier New" size=2>43:










face="Courier New" size=2>44:     public static
void main(String[] args) {










face="Courier New"
size=2>45:         try
{










face="Courier New"
size=2>46:            
SyncBlock sb = new SyncBlock();










face="Courier New" size=2>47:    










face="Courier New"
size=2>48:            
Thread t1 = launch(sb, “T1”);










face="Courier New"
size=2>49:            
Thread.sleep(500);










face="Courier New" size=2>50:










face="Courier New"
size=2>51:            
Thread t2 = launch(sb, “T2”);










face="Courier New"
size=2>52:            
Thread t3 = launch(sb, “T3”);










face="Courier New" size=2>53:










face="Courier New"
size=2>54:            
Thread.sleep(1000);










face="Courier New" size=2>55:










face="Courier New"
size=2>56:            
print(“about to interrupt T2”);










face="Courier New"
size=2>57:            
t2.interrupt();










face="Courier New"
size=2>58:            
print(“just interrupted T2”);










face="Courier New" size=2>59:










face="Courier New"
size=2>60:         } catch (
InterruptedException x ) {










face="Courier New"
size=2>61:            
x.printStackTrace();










face="Courier New"
size=2>62:        
}










face="Courier New" size=2>63:     }










face="Courier New" size=2>64: }












size=2>The size=2>SyncBlock class
has two parts: the instance-specific code (lines 2–19) and the
size=2>static methods
used for the demonstration (lines 21–63). The
color=#010100 face="Courier New" size=2>static color=#010100 face=Arial size=2> method face="Courier New" size=2>print() face=Arial size=2> (lines 21–24) is used by both parts to print
detailed messages.












size=2>In size=2>main() (lines
44–63), an instance of
size=2>SyncBlock is
constructed to be shared by three threads (line 46). The
size=2>launch() method
(lines 26–42) takes a reference to this shared instance, creates a
new
size=2>Runnable (and
size=2>Thread to run
it), and uses this thread to simply invoke the
color=#010100 face="Courier New" size=2>doStuff() color=#010100 face=Arial size=2> method of color=#010100 face="Courier New" size=2>SyncBlock color=#010100 face=Arial size=2> (line 34). A reference to this
size=2>Thread is
returned by
size=2>launch() size=2>.












size=2>The size=2>doStuff() method
(lines 8–19) prints a message (lines 9–10) and then tries to acquire
an exclusive lock on
size=2>longLock (line
12). Once a thread gets exclusive access, it prints a message,
sleeps for 10 seconds, and prints another message (lines 13–17).
While one thread is inside sleeping for 10 seconds, other threads
line up and block waiting to get exclusive access.












size=2>Back in size=2>main(), thread
T1 color=#010100 face=Arial size=2> is launched (line 48). It gets
right into the
size=2>synchronized
block of
size=2>doStuff()
because no other threads are competing with it. After a half-second
sleep,
size=2>T2 is launched,
immediately followed by
face="Courier New" size=2>T3 size=2> (lines 49–52). These two threads block on the color=#010100 face="Courier New" size=2>synchronized color=#010100 face=Arial size=2> statement (line 12) waiting for
T1 color=#010100 face=Arial size=2> to finish up and release the
lock.












size=2>Meanwhile, the size=2>main thread
sleeps for one second to be sure that
face="Courier New" size=2>T2 size=2> and size=2>T3 get a chance
to block (line 54). Next, the
face="Courier New" size=2>main size=2> thread interrupts face="Courier New" size=2>T2 size=2> (line 57) hoping to free it from its blocked state. As
Listing 17.5 shows, this interrupt does
face=Arial size=2>not size=2> free size=2>T2.












size=2>Listing 17.5  Possible Output from
SyncBlock












face="Courier New" size=2>1: T1: in run()










face="Courier New" size=2>2: T1: about to try to get exclusive
access to longLock










face="Courier New" size=2>3: T1: got exclusive access to
longLock










face="Courier New" size=2>4: T3: in run()










face="Courier New" size=2>5: T3: about to try to get exclusive
access to longLock










face="Courier New" size=2>6: T2: in run()










face="Courier New" size=2>7: T2: about to try to get exclusive
access to longLock










face="Courier New" size=2>8: main: about to interrupt T2










face="Courier New" size=2>9: main: just interrupted T2










face="Courier New" size=2>10: T1: about to relinquish exclusive
access to longLock










face="Courier New" size=2>11: T3: got exclusive access to
longLock










face="Courier New" size=2>12: T3: about to relinquish exclusive
access to longLock










face="Courier New" size=2>13: T2: got exclusive access to
longLock










face="Courier New" size=2>14: T2: about to relinquish exclusive
access to longLock












size=2>Lines 8 and 9 are printed just before and just after
T2 color=#010100 face=Arial size=2> is interrupted. As you can see,
this does not have the desired effect;
face="Courier New" size=2>T2 size=2> continues to wait and ultimately gains access (lines
13–14).












size=4>InterruptibleSyncBlock












face="Courier New" size=2>InterruptibleSyncBlock color=#010100 face=Arial size=2> (see Listing 17.6) uses the
functionality of
size=2>BooleanLock to
have threads blocked on an interruptible
face="Courier New" size=2>wait() face=Arial size=2> statement instead of a non-interruptible
size=2>synchronized
statement.












size=2>Listing 17.6  InterruptibleSyncBlock.java—An
Alternative Waiting List












face="Courier New" size=2>1: public class InterruptibleSyncBlock
extends Object {










face="Courier New" size=2>2:     private Object
longLock;










face="Courier New" size=2>3:     private
BooleanLock busyLock;










face="Courier New" size=2>4:










face="Courier New" size=2>5:     public
InterruptibleSyncBlock() {










face="Courier New"
size=2>6:         longLock =
new Object();










face="Courier New"
size=2>7:        
busyLock =
new BooleanLock(false);










face="Courier New" size=2>8:     }










face="Courier New" size=2>9:










face="Courier New" size=2>10:     public void
doStuff() throws InterruptedException {










face="Courier New"
size=2>11:        
print(“about to try to get exclusive access “ +










face="Courier New"
size=2>12:                
“to busyLock”);










face="Courier New"
size=2>13:        
size=2>busyLock.waitToSetTrue(0);










face="Courier New" size=2>14:










face="Courier New"
size=2>15:         try
{










face="Courier New"
size=2>16:            
print(“about to try to get exclusive access “ +










face="Courier New"
size=2>17:                    
“to longLock”);










face="Courier New"
size=2>18:            
synchronized ( longLock ) {










face="Courier New"
size=2>19:                
print(“got exclusive access to longLock”);










face="Courier New"
size=2>20:                
try {










face="Courier New"
size=2>21:                    
Thread.sleep(10000);










face="Courier New"
size=2>22:                
} catch ( InterruptedException x ) {










face="Courier New"
size=2>23:                    
// ignore










face="Courier New"
size=2>24:                
}










face="Courier New"
size=2>25:                
print(“about to relinquish exclusive access “ +










face="Courier New"
size=2>26:                        
“to longLock”);










face="Courier New"
size=2>27:            
size=2>}










face="Courier New"
size=2>28:         } finally
{










face="Courier New"
size=2>29:            
print(“about to free up busyLock”);










face="Courier New"
size=2>30:            
size=2>busyLock.setValue(false);










face="Courier New"
size=2>31:        
}










face="Courier New" size=2>32:     }










face="Courier New" size=2>33:










face="Courier New" size=2>34:     private static
void print(String msg) {










face="Courier New"
size=2>35:         String
name = Thread.currentThread().getName();










face="Courier New"
size=2>36:        
System.err.println(name + “: “ + msg);










face="Courier New" size=2>37:     }










face="Courier New" size=2>38:










face="Courier New" size=2>39:     private static
Thread launch(










face="Courier New"
size=2>40:                
final InterruptibleSyncBlock sb,










face="Courier New"
size=2>41:                
String name










face="Courier New"
size=2>42:            
) {










face="Courier New" size=2>43:










face="Courier New"
size=2>44:         Runnable
r = new Runnable() {










face="Courier New"
size=2>45:                
public void run() {










face="Courier New"
size=2>46:                    
print(“in run()”);










face="Courier New"
size=2>47:                    
try {










face="Courier New"
size=2>48:                        
sb.doStuff();










face="Courier New"
size=2>49:                    
} catch ( InterruptedException x ) {










face="Courier New"
size=2>50:                        
print(“InterruptedException thrown “ +










face="Courier New"
size=2>51:                                
“from doStuff()”);










face="Courier New"
size=2>52:                    
}










face="Courier New"
size=2>53:                
}










face="Courier New"
size=2>54:            
};










face="Courier New"
size=2>55:        










face="Courier New"
size=2>56:         Thread t
= new Thread(r, name);










face="Courier New"
size=2>57:        
t.start();










face="Courier New" size=2>58:










face="Courier New"
size=2>59:         return
t;










face="Courier New" size=2>60:     }










face="Courier New" size=2>61:










face="Courier New" size=2>62:     public static
void main(String[] args) {










face="Courier New"
size=2>63:         try
{










face="Courier New"
size=2>64:            
InterruptibleSyncBlock sb =










face="Courier New"
size=2>65:                    
new InterruptibleSyncBlock();










face="Courier New" size=2>66:    










face="Courier New"
size=2>67:            
Thread t1 = launch(sb, “T1”);










face="Courier New"
size=2>68:            
Thread.sleep(500);










face="Courier New" size=2>69:










face="Courier New"
size=2>70:            
Thread t2 = launch(sb, “T2”);










face="Courier New"
size=2>71:            
Thread t3 = launch(sb, “T3”);










face="Courier New" size=2>72:










face="Courier New"
size=2>73:            
Thread.sleep(1000);










face="Courier New" size=2>74:










face="Courier New"
size=2>75:            
print(“about to interrupt T2”);










face="Courier New"
size=2>76:            
t2.interrupt();










face="Courier New"
size=2>77:            
print(“just interrupted T2”);










face="Courier New" size=2>78:










face="Courier New"
size=2>79:         } catch (
InterruptedException x ) {










face="Courier New"
size=2>80:            
x.printStackTrace();










face="Courier New"
size=2>81:        
}










face="Courier New" size=2>82:     }










face="Courier New" size=2>83: }












face="Courier New" size=2>InterruptibleSyncBlock color=#010100 face=Arial size=2> is very similar to color=#010100 face="Courier New" size=2>SyncBlock color=#010100 face=Arial size=2>, except for a few enhancements
inside the
size=2>doStuff() method
(lines 10–32). A new member variable
face="Courier New" size=2>busyLock face=Arial size=2> has been added (line 3), and it is an instance of
size=2>BooleanLock
(line 7). It is used inside
face="Courier New" size=2>doStuff() face=Arial size=2> to control concurrent access. The color=#010100 face="Courier New" size=2>waitToSetTrue() color=#010100 face=Arial size=2> method is invoked by each thread as
it enters
size=2>doStuff() (line
13). Because a timeout of
face="Courier New" size=2>0 size=2> is passed, threads will wait forever to get their turn—or at
least until they are interrupted. When one of the threads gets
notified that it can set the state to
face="Courier New" size=2>true size=2>, it does so and returns from face="Courier New" size=2>waitToSetTrue() face=Arial size=2>. All the other threads will get notified, but
will see that some other thread has beat them and will go back to
waiting inside
size=2>waitToSetTrue().
The thread that gets to set
face="Courier New" size=2>busyLock face=Arial size=2> to size=2>true then
proceeds into the
size=2>try block (lines
15–27). No matter how this thread leaves the
color=#010100 face="Courier New" size=2>try color=#010100 face=Arial size=2> block (even by throwing an
size=2>Exception or
size=2>Error), it will
enter the
size=2>finally block
(lines 28–31) and set
size=2>busyLock back to
size=2>false (line 30)
to allow another thread to get into the exclusive section.












size=2>Because size=2>busyLock is now
protecting the
size=2>synchronized
block, there might not be any need to synchronize on
color=#010100 face="Courier New" size=2>longLock color=#010100 face=Arial size=2>. If there are other areas where
size=2>longLock was
being used to control concurrent access (which is not the case in
the example), you have two options. One option is to protect
concurrent access by using
face="Courier New" size=2>busyLock face=Arial size=2> everywhere that face="Courier New" size=2>longLock face=Arial size=2> was previously used. The other option is to
continue to have
size=2>longLock protect
all sections (as in this example) and to have
color=#010100 face="Courier New" size=2>busyLock color=#010100 face=Arial size=2> provide a preliminary,
interruptible barrier for long-running sections of code.












size=2>Listing 17.7 shows possible output when color=#010100 face="Courier New"
size=2>InterruptibleSyncBlock
size=2> is run.












size=2>Listing 17.7  Possible Output from
InterruptibleSyncBlock












face="Courier New" size=2>1: T1: in run()










face="Courier New" size=2>2: T1: about to try to get exclusive
access to busyLock










face="Courier New" size=2>3: T1: about to try to get exclusive
access to longLock










face="Courier New" size=2>4: T1: got exclusive access to
longLock










face="Courier New" size=2>5: T2: in run()










face="Courier New" size=2>6: T2: about to try to get exclusive
access to busyLock










face="Courier New" size=2>7: T3: in run()










face="Courier New" size=2>8: T3: about to try to get exclusive
access to busyLock










face="Courier New" size=2>9: main: about to interrupt T2










face="Courier New" size=2>10: main: just interrupted T2










face="Courier New" size=2>11: T2: InterruptedException thrown from
doStuff()










face="Courier New" size=2>12: T1: about to relinquish exclusive
access to longLock










face="Courier New" size=2>13: T1: about to free up
busyLock










face="Courier New" size=2>14: T3: about to try to get exclusive
access to longLock










face="Courier New" size=2>15: T3: got exclusive access to
longLock










face="Courier New" size=2>16: T3: about to relinquish exclusive
access to longLock










face="Courier New" size=2>17: T3: about to free up
busyLock












size=2>This time, when size=2>T2 is
interrupted (lines 9–10), it throws an
face="Courier New" size=2>InterruptedException color=#010100 face=Arial size=2> (line 11) and is broken out of the
blocked state.












size=2>This use of size=2>BooleanLock is
great when a
size=2>synchronized
section of code might run for a long time. Also consider passing a
non-zero timeout so that threads will not block forever waiting for
exclusive access.












valign="top" bgcolor="#FFFFFF">

Toc



No comments: