Using ThreadLocal and InheritableThreadLocal

Chapter 9 - Threads and Swing

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

The Need for Worker Threads in a GUI Setting
The event thread plays a critical role in an application with a graphical interface. Code that will be executed by the event-handling thread should be relatively brief and nonblocking. If the event-handling thread is blocked in a section of code for a while, no other events can be processed !
This is especially important in a client/server application (even more so in an n- tier application). Imagine a situation where the client is a graphical application with a Search button. When this button is clicked, a request is made over the network to the server for the results. The server produces the results and sends this information back down to the client. The client then displays this result information on the GUI. To be safe, the event thread needs to be the thread that gathers the information from the GUI for the search. The event thread also needs to be the thread that displays the results. But does the event thread have to send the request over the network? No, it does not, and should not.
The BalanceLookupCantCancel class  (see Listing 9.6) shows what happens when the event thread is used to fulfill a request that takes a long time. This simple graphical client simulates a call over the network by sleeping for five seconds before returning the account balance.
Listing 9.6  BalanceLookupCantCancel.javaOverusing the Event Thread
  1: import java.awt.*;
  2: import java.awt.event.*;
  3: import javax.swing.*;
  4:
  5: public class BalanceLookupCantCancel extends JPanel {
  6:     private JTextField acctTF;
  7:     private JTextField pinTF;
  8:     private JButton searchB;
  9:     private JButton cancelB;
10:     private JLabel balanceL;
11:
12:     public BalanceLookupCantCancel() {
13:         buildGUI();
14:         hookupEvents();
15:     }
16:
17:     private void buildGUI() {
18:         JLabel acctL = new JLabel(Account Number:);
19:         JLabel pinL = new JLabel(PIN:);
20:         acctTF = new JTextField(12);
21:         pinTF = new JTextField(4);
22:
23:         JPanel dataEntryP = new JPanel();
24:         dataEntryP.setLayout(new FlowLayout(FlowLayout.CENTER));
25:         dataEntryP.add(acctL);
26:         dataEntryP.add(acctTF);
27:         dataEntryP.add(pinL);
28:         dataEntryP.add(pinTF);
29:
30:         searchB = new JButton(Search);
31:         cancelB = new JButton(Cancel Search);
32:         cancelB.setEnabled(false);
33:
34:         JPanel innerButtonP = new JPanel();
35:         innerButtonP.setLayout(new GridLayout(1, -1, 5, 5));
36:         innerButtonP.add(searchB);
37:         innerButtonP.add(cancelB);
38:
39:         JPanel buttonP = new JPanel();
40:         buttonP.setLayout(new FlowLayout(FlowLayout.CENTER));
41:         buttonP.add(innerButtonP);
42:
43:         JLabel balancePrefixL = new JLabel(Account Balance:);
44:         balanceL = new JLabel(BALANCE UNKNOWN);
45:
46:         JPanel balanceP = new JPanel();
47:         balanceP.setLayout(new FlowLayout(FlowLayout.CENTER));
48:         balanceP.add(balancePrefixL);
49:         balanceP.add(balanceL);
50:
51:         JPanel northP = new JPanel();
52:         northP.setLayout(new GridLayout(-1, 1, 5, 5));
53:         northP.add(dataEntryP);
54:         northP.add(buttonP);
55:         northP.add(balanceP);
56:
57:         setLayout(new BorderLayout());
58:         add(northP, BorderLayout.NORTH);
59:     }
60:
61:     private void hookupEvents() {
62:         searchB.addActionListener(new ActionListener() {
63:                 public void actionPerformed(ActionEvent e) {
64:                     search();
65:                 }
66:             });
67:
68:         cancelB.addActionListener(new ActionListener() {
69:                 public void actionPerformed(ActionEvent e) {
70:                     cancelSearch();
71:                 }
72:             });
73:     }
74:
75:     private void search() {
76:         // better be called by event thread!
77:         searchB.setEnabled(false);
78:         cancelB.setEnabled(true);
79:         balanceL.setText(SEARCHING ...);
80:
81:         // get a snapshot of this info in case it changes
82:         String acct = acctTF.getText();
83:         String pin = pinTF.getText();
84:
85:         String bal = lookupBalance(acct, pin);
86:         setBalance(bal);
87:     }
88:
89:     private String lookupBalance(String acct, String pin) {
90:         try {
91:             // Simulate a lengthy search that takes 5 seconds
92:             // to communicate over the network.
93:             Thread.sleep(5000);
94:
95:             // result retrieved, return it
96:             return 1,234.56;
97:         } catch (InterruptedException x) {
98:             return SEARCH CANCELLED;
99:         }
100:     }
101:
102:     private void setBalance(String newBalance) {
103:         // better be called by event thread!
104:         balanceL.setText(newBalance);
105:         cancelB.setEnabled(false);
106:         searchB.setEnabled(true);
107:     }
108:
109:     private void cancelSearch() {
110:         System.out.println(in cancelSearch());
111:         // Heres where the code to cancel would go if this
112:         // could ever be called!
113:     }
114:
115:     public static void main(String[] args) {
116:         BalanceLookupCantCancel bl =
117:                 new BalanceLookupCantCancel();
118:
119:         JFrame f = new JFrame(Balance Lookup - Cant Cancel);
120:         f.addWindowListener(new WindowAdapter() {
121:                 public void windowClosing(WindowEvent e) {
122:                     System.exit(0);
123:                 }
124:             });
125:
126:         f.setContentPane(bl);
127:         f.setSize(400, 150);
128:         f.setVisible(true);
129:     }
130: }
Most of BalanceLookupCantCancel (lines 173, 115129) is dedicated to constructing the GUI. In hookupEvents() (lines 6173), an event handler for each button is added. When the search button searchB is clicked, the search() method is called (lines 6365). When the cancel button cancelB is clicked, cancelSearch() is called (lines 6971).
Inside search() (lines 7587), the Search button is disabled, the Cancel Search button is enabled, and the balance label is set to SEARCHING ... while the search is in progress (lines 7778). The event thread is used to gather the account
number and PIN number from the fields (lines 8283). These strings are passed into lookupBalance() , and the balance found is returned and shown on the screen (lines 8586).
The lookupBalance() method (lines 89100) is used to simulate a lookup over a network connection. It sleeps for five seconds to simulate the delay for lookup and then returns 1,234.56 for every account. If the thread that called lookupBalance() is interrupted while the lookup is in progress (sleeping), it returns the SEARCH CANCELLED string instead of the balance. This is just a simulation; of course, a real system would do something more useful.
The setBalance() method (lines 102107) is used to update the balance, disable the Cancel Search button, and enable the Search button again. The cancelSearch() method (lines 109113) would normally be used to stop the search process, but in this example, it never gets called.
When the event thread calls search() , it blocks until the balance is retrieved and set. Keeping the event thread tied up for that long is a bad idea. And in this example, it prevents the Cancel Search button from being enabled.
Figure 9.1 shows how the application looks when it is first started. Notice that the Cancel Search button is disabled and that the balance label indicates that the balance is unknown.
After the user enters an account number and a PIN and clicks the Search button, the application looks like Figure 9.2. The window continues to look like that for about 5 seconds while the across-the-network lookup is simulated. Notice the following points:
  The SEARCHING ... message was not displayed in the balance label.
  The Cancel Search button was never enabled.
  The Search button stayed pressed in the whole time.
For the whole time that the lookup was going on, the GUI was unresponsive the window couldnt even be closed. In particular, the Cancel Search button was never enabled. The event thread was tied up doing the long-running lookup and could not respond to user events. Obviously, this is not a good design.
Figure 9.3 shows what the application window looks like after the 5 seconds have elapsed. Here everything is as expected. The Search button is enabled, the Cancel Search button is disabled, and the balance label shows 1,234.56 .
Figure 9.1: BalanceLookupCantCancel just after startup.
Figure 9.2: BalanceLookupCantCancel after the Search button is clicked.
Figure 9.3: BalanceLookupCantCancel when the lookup finally completes.

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