The read() Method Ignores Interrupts and Stop Requests

Chapter 17 - The BooleanLock Utility

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Avoiding Blocking on synchronized
The 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 synchronized 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 synchronized section. This can become a problem when the work done inside a synchronized section takes a relatively long time to complete. The examples in this section demonstrate how to work around this situation.
SyncBlock
SyncBlock (see Listing 17.4) demonstrates how threads blocked on a synchronized statement ignore interrupt requests .
Listing 17.4  SyncBlock.javaThreads Blocked on synchronized Ignore Interrupts
1: public class SyncBlock extends Object {
2:     private Object longLock;
3:
4:     public SyncBlock() {
5:         longLock = new Object();
6:     }
7:
8:     public void doStuff() {
9:         print(about to try to get exclusive access +
10:                 to longLock);
11:
12:         synchronized (longLock) {
13:             print(got exclusive access to longLock);
14:             try { Thread.sleep(10000); }
15:             catch (InterruptedException x) { }
16:             print(about to relinquish exclusive access to +
17:                     longLock);
18:         }
19:     }
20:
21:     private static void print(String msg) {
22:         String name = Thread.currentThread().getName();
23:         System.err.println(name + : + msg);
24:     }
25:
26:     private static Thread launch(
27:                 final SyncBlock sb,
28:                 String name
29:            ) {
30:
31:         Runnable r = new Runnable() {
32:                 public void run() {
33:                     print(in run());
34:                     sb.doStuff();
35:                 }
36:             };
37:
38:         Thread t = new Thread(r, name);
39:         t.start();
40:
41:         return t;
42:     }
43:
44:     public static void main(String[] args) {
45:         try {
46:             SyncBlock sb = new SyncBlock();
47:    
48:             Thread t1 = launch(sb, T1);
49:             Thread.sleep(500);
50:
51:             Thread t2 = launch(sb, T2);
52:             Thread t3 = launch(sb, T3);
53:
54:             Thread.sleep(1000);
55:
56:             print(about to interrupt T2);
57:             t2.interrupt();
58:             print(just interrupted T2);
59:
60:         } catch (InterruptedException x) {
61:             x.printStackTrace();
62:         }
63:     }
64: }
The SyncBlock class has two parts : the instance-specific code (lines 219) and the static methods used for the demonstration (lines 2163). The static method print() (lines 2124) is used by both parts to print detailed messages.
In main() (lines 4463), an instance of SyncBlock is constructed to be shared by three threads (line 46). The launch() method (lines 2642) takes a reference to this shared instance, creates a new Runnable (and Thread to run it), and uses this thread to simply invoke the doStuff() method of SyncBlock (line 34). A reference to this Thread is returned by launch() .
The doStuff() method (lines 819) prints a message (lines 910) and then tries to acquire an exclusive lock on longLock (line 12). Once a thread gets exclusive access, it prints a message, sleeps for 10 seconds, and prints another message (lines 1317). While one thread is inside sleeping for 10 seconds, other threads line up and block waiting to get exclusive access.
Back in main() , thread T1 is launched (line 48). It gets right into the synchronized block of doStuff() because no other threads are competing with it. After a half-second sleep, T2 is launched, immediately followed by T3 (lines 4952). These two threads block on the synchronized statement (line 12) waiting for T1 to finish up and release the lock.
Meanwhile, the main thread sleeps for one second to be sure that T2 and T3 get a chance to block (line 54). Next, the main thread interrupts T2 (line 57) hoping to free it from its blocked state. As Listing 17.5 shows, this interrupt does not free T2 .
Listing 17.5  Possible Output from SyncBlock
1: T1: in run()
2: T1: about to try to get exclusive access to longLock
3: T1: got exclusive access to longLock
4: T3: in run()
5: T3: about to try to get exclusive access to longLock
6: T2: in run()
7: T2: about to try to get exclusive access to longLock
8: main: about to interrupt T2
9: main: just interrupted T2
10: T1: about to relinquish exclusive access to longLock
11: T3: got exclusive access to longLock
12: T3: about to relinquish exclusive access to longLock
13: T2: got exclusive access to longLock
14: T2: about to relinquish exclusive access to longLock
Lines 8 and 9 are printed just before and just after T2 is interrupted. As you can see, this does not have the desired effect; T2 continues to wait and ultimately gains access (lines 1314).
InterruptibleSyncBlock
InterruptibleSyncBlock (see Listing 17.6) uses the functionality of BooleanLock to have threads blocked on an interruptible wait() statement instead of a non-interruptible synchronized statement.
Listing 17.6  InterruptibleSyncBlock.javaAn Alternative Waiting List
1: public class InterruptibleSyncBlock extends Object {
2:     private Object longLock;
3:     private BooleanLock busyLock;
4:
5:     public InterruptibleSyncBlock() {
6:         longLock = new Object();
7:         busyLock = new BooleanLock(false);
8:     }
9:
10:     public void doStuff() throws InterruptedException {
11:         print(about to try to get exclusive access +
12:                 to busyLock);
13:         busyLock.waitToSetTrue(0);
14:
15:         try {
16:             print(about to try to get exclusive access +
17:                     to longLock);
18:             synchronized (longLock) {
19:                 print(got exclusive access to longLock);
20:                 try {
21:                     Thread.sleep(10000);
22:                 } catch (InterruptedException x) {
23:                     // ignore
24:                 }
25:                 print(about to relinquish exclusive access +
26:                         to longLock);
27:             }
28:         } finally {
29:             print(about to free up busyLock);
30:             busyLock.setValue(false);
31:         }
32:     }
33:
34:     private static void print(String msg) {
35:         String name = Thread.currentThread().getName();
36:         System.err.println(name + : + msg);
37:     }
38:
39:     private static Thread launch(
40:                 final InterruptibleSyncBlock sb,
41:                 String name
42:            ) {
43:
44:         Runnable r = new Runnable() {
45:                 public void run() {
46:                     print(in run());
47:                     try {
48:                         sb.doStuff();
49:                     } catch (InterruptedException x) {
50:                         print(InterruptedException thrown +
51:                                 from doStuff());
52:                     }
53:                 }
54:             };
55:        
56:         Thread t = new Thread(r, name);
57:         t.start();
58:
59:         return t;
60:     }
61:
62:     public static void main(String[] args) {
63:         try {
64:             InterruptibleSyncBlock sb =
65:                     new InterruptibleSyncBlock();
66:    
67:             Thread t1 = launch(sb, T1);
68:             Thread.sleep(500);
69:
70:             Thread t2 = launch(sb, T2);
71:             Thread t3 = launch(sb, T3);
72:
73:             Thread.sleep(1000);
74:
75:             print(about to interrupt T2);
76:             t2.interrupt();
77:             print(just interrupted T2);
78:
79:         } catch (InterruptedException x) {
80:             x.printStackTrace();
81:         }
82:     }
83: }
InterruptibleSyncBlock is very similar to SyncBlock , except for a few enhancements inside the doStuff() method (lines 1032). A new member variable busyLock has been added (line 3), and it is an instance of BooleanLock (line 7). It is used inside doStuff() to control concurrent access. The waitToSetTrue() method is invoked by each thread as it enters doStuff() (line 13). Because a timeout of 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 true , it does so and returns from waitToSetTrue() . All the other threads will get notified, but will see that some other thread has beat them and will go back to waiting inside waitToSetTrue() . The thread that gets to set busyLock to true then proceeds into the try block (lines 1527). No matter how this thread leaves the try block (even by throwing an Exception or Error ), it will enter the finally block (lines 2831) and set busyLock back to false (line 30) to allow another thread to get into the exclusive section.
Because busyLock is now protecting the synchronized block, there might not be any need to synchronize on longLock . If there are other areas where 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 busyLock everywhere that longLock was previously used. The other option is to continue to have longLock protect all sections (as in this example) and to have busyLock provide a preliminary, interruptible barrier for long-running sections of code.
Listing 17.7 shows possible output when InterruptibleSyncBlock is run.
Listing 17.7  Possible Output from InterruptibleSyncBlock
1: T1: in run()
2: T1: about to try to get exclusive access to busyLock
3: T1: about to try to get exclusive access to longLock
4: T1: got exclusive access to longLock
5: T2: in run()
6: T2: about to try to get exclusive access to busyLock
7: T3: in run()
8: T3: about to try to get exclusive access to busyLock
9: main: about to interrupt T2
10: main: just interrupted T2
11: T2: InterruptedException thrown from doStuff()
12: T1: about to relinquish exclusive access to longLock
13: T1: about to free up busyLock
14: T3: about to try to get exclusive access to longLock
15: T3: got exclusive access to longLock
16: T3: about to relinquish exclusive access to longLock
17: T3: about to free up busyLock
This time, when T2 is interrupted (lines 910), it throws an InterruptedException (line 11) and is broken out of the blocked state.
This use of BooleanLock is great when a 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.

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