By Jonathan Simon
Original: http://today.java.net/pub/a/today.java.net/pub/a/today.java.net/pub/a/today.html? Page = 1 Incorrect SWING thread is the main run, no response, and unstable Swing application main one of the reasons. This is caused by many reasons, from the developer's misunderstanding of the Swing single-threaded model to ensure the correct thread execution. Even for a lot of effort to Swing threads, the application thread logic is also difficult to understand and maintain. This article describes how to use event-driven programming in developing Swing applications to greatly streamline development, maintenance, and provide high spirituality.
background
Since we are to simplify the thread of Swing applications, let's take a look at how the Swing thread works, why it is necessary. Swing API is designed around a single thread model. This means that the SWING component must always modify and manipulate through the same thread. Why use a single-threaded model, which has many reasons, including development costs and synchronous Swing complexity - this will cause a dull API. In order to achieve a single-threaded model, there is a dedicated thread for interacting with the Swing component. This thread is a familiar SWING thread, AWT (sometimes pronounces "an" ought ") thread, or event assignment thread. In the following part of this article, I choose a swings thread called.
Since the Swing thread is a unique thread that interacts with the Swing component, it has been given a lot of responsibility. All drawing and graphics, mouse events, component events, buttons events, and all other events occur in Swing threads. Because Swing threads have been very heavy, there is a problem when too many other work is processed in a swing thread. The most common location that will cause this problem is where non-SWING processing, like the ActionListener, such as JButton, in an event listener method, database lookup. Since the ActionListener's actionPerformed () method is automatically executed in the Swing thread, the database lookup will also be executed in the Swing thread. This will occupy Swing's work and prevent it from handling other tasks - image, respond to mouse movement, processing buttons events, and application scaling. Users think that it is dead, but actually is not the case. Executing the code in the appropriate thread is very important to ensure that the system is implemented normally.
Since we have seen how important the code to execute Swing applications in the appropriate thread is now how we do these threads. Let's take a look at the standard mechanism to put the code and remove the SWING thread. In the process of telling, I will highlight several problems and difficulties related to standard mechanisms. As we see, most of the issues come from code models that try to achieve synchronization on an asynchronous Swing thread model. From there, we will see how to modify our example to event-driven - transplant the entire way to asynchronous models.
General Swing thread solution
Let us start with a most common Swing thread error. We will try to use standard technologies to fix this problem. In this process, we will see the complexity and common difficulties of achieving the correct swings thread. Also, pay attention to the correction of this Swing thread problem, many intermediate examples cannot work. In the example, I started with // broker where the code failed. Ok, now, let us enter our example.
Suppose we are performing book findings. We have a simple user interface, including a lookup domain, a lookup button, and an output text area. This interface is shown in Figure 1. Don't criticize my UI design, this is really ugly, I admit.
Figure 1. Basic query user interface
User input book title, authors, or other conditions, then display a list of results. The following code example demonstrates that the ActionListener of the button calls the lookup () method in the same thread. In these examples, I used Thread.Sleep () to sleep for 5 seconds as a placement outside. The result of thread sleep is equivalent to a time-consuming 5 second synchronization server call. Private
Void searchButton_ActionPerformed () {
Outputta.settext
"Searching for:"
SearchTf.gettext ());
// broken !! Too Much Work in The Swing Thread
String [] results = lookup (SearchTf.getText ());
Outputta.settext
"" "
FOR
INT i = 0; i String result = results [i]; Outputta.Settext (Outputta.getText () '/ n' result); } } If you run this code ( The complete code can be downloaded here), you will immediately find some problems. Figure 2 shows a screenshot in the search run. Figure 2. Find in Swing thread Note that the Go button appears to be pressed. This is because the ActionPerformed method notifies the button to draw as an unpubstated appearance, but has not returned. You will also find the string "abcde" to look for "ABCDE" does not appear in the text area. SearchButton_ActionPerformed Line 1 code Sets the text area to the string to find. However, pay attention to Swing heavy painting is not immediately executed. Instead, place the Heavy Picture Request to Swing Thread Processing in the Swing Event Queue. But here, we occupy the Swing thread because of the lookup processing, so it can't go back immediately. To correct these issues, let's move the lookup operation into a non-Swing thread. What we first think is to let the entire method execute in a new thread. The problem is the Swing component. The text area in this example can only be edited from the Swing thread. Here is the modified SearchButton_ActionPerformed method: Private Void searchButton_ActionPerformed () { Outputta.settext "Searching for:" SearchTf.gettext ()); // the string [] [] is buy to allow access to // setting the results from an inner class Final String [] [] results = NEW STRING [1] [1]; New thread () { public Void Run () { Results [0] = Lookup (SearchTf.getText ()); } } .start (); Outputta.settext "" " FOR INT i = 0; i String result = results [0] [i]; Outputta.Settext (Outputta.getText () '/ n' result); } } This method has many problems. Note Final String [] []. This is an alternative to handling an anonymous internal class and scope. Basically, in an anonymous internal classes, any variables defined in the external surround scope need to be defined as Final. You can have a variable to solve this problem by creating an array. In this case, you can create an array for final, modify the elements in the array, rather than the reference to the array itself. Since we have solved this problem, let us enter the real problem. Figure 3 shows the case where this code is running: Figure 3. Find outside the Swing thread The interface shows a NULL because the display code is processed before the lookup code is completed. This is because the code block continues to execute once the new thread is started, not waiting for the thread execution. This is one of those strange concurrent blocks, and below will write it into a method to make it real implementation. There are two ways to help us solve these problems in the Swingutilities class: InvokerLater () and InvokeAndwait (). Each method uses a runnable as a parameter and executes it in the Swing thread. The invokeAndwait () method is blocked until the runnnable is complete; InvokeLater () performs Runnable asynchronously. InvokeAndwait () is generally not approved because it may cause serious thread dead locks, causing serious damage to your application. So let's place it while using the invokelater () method. To correct the last variable variable scoPing and execution order, we must transfer the GetText () and setText () method of the text area into a runnable, only after the query result is returned, and execute it in the Swing thread. We can make this way, create an anonymous runnable to invokelater (), including the text area operation after runnnable in the new thread. This ensures that the Swing code will not be executed before the lookup ends. The following is the corrected code: Private Void searchButton_ActionPerformed () { Outputta.settext "Searching for:" SearchTf.gettext ()); Final String [] [] results = NEW STRING [1] [1]; New thread () { public Void Run () { // Get results. Results [0] = Lookup (SearchTf.getText ()); // send runnable to the swing thread // the runnable is queued after the // Results Are Returned Swingutilities.invokelater ( New runnable () { public Void Run () { // Now We're in the swing thread Outputta.settext "" " FOR INT i = 0; I i ) { String result = results [0] [i]; Outputta.settext Outputta.gettext () '/ n' result); } } } ); } } .start (); } This can work, but this is very headache. We have to perform the order performed by anonymous threads, we have to deal with difficult Scooping issues. There are many problems, and this is just a very simple example, we have encountered a series of problems such as role domain, variable delivery, and execution order. As is a more complex problem, including several layers of nested, shared references and specified execution order. This method will soon be out of control. problem We attempt to enforce synchronous execution through asynchronous models - attempting to put a square bolt into a circular air. Only we try to do this, we will constantly encounter these problems. From my experience, you can tell you that these codes are difficult to read, it is difficult to maintain, and it is easy to go wrong. This looks a common problem, so there must be a standard way to solve, right? Some frameworks have been used to manage Swing complex, so let's quickly preview what they can do. A solution that can be obtained is Foxtrot, a framework written by Biorn Steedom, which can be obtained on SourceForge. It uses an object called Worker to control non-Swing tasks in non-Swing threads, block until the non-Swing task is completed. It simplifies Swing threads, allowing you to write synchronous code and switch directly to Swing threads and non-Swing threads. Here is an example of its site: public Void ActionPerformed (ActionEvent E) { Button.setText ( "Sleeping ..."); String text = NULL; Try { TEXT = (String) Worker.post New task () { Public Object Run () Throws Exception { Thread.sleep (10000); Return "SLEPT!"; } }); } Catch (Exception X) ... Button.Settext (Text); Somethingelse (); } Note how it solves these problems. We can easily pass the outgoing variables in the Swing thread. Also, the code block looks correct - first execution first. But there are still some problems barriers to prevent use of synchronous asynchronous solutions. One of the foxtrot is an abnormal management. Using Foxtrot, you must capture the Exception each time you call Worker. This is a product that will execute a proxy to solve synchronous pairs of asynchronous problems. Also in a very similar way, I have also created a framework, I call it a chained runnable engine, which also suffers from similar synchronous pairs of asynchronous problems. With this frame, you will create a collection of runnable that will be executed by the engine. Every runnable has an indicator to tell the engine whether it should be executed in a swing thread or another thread. The engine also guarantees that Runnable is executed in the correct order. So Runnable # 2 will not put into the queue until runnable # 1 is executed. Also, it supports variables from runnable to Runnable delivery in the form of HashMap. On the surface, it seems to solve our main problem. But when you go deep into it, the same problem is coming out. Essentially, we did not change anything above - we just hide complexity behind the engine. Because of the exponential growth RunNable, the code writing will become very boring, and these RunNable are often coupled to each other. The non-type HashMap variable transmission between runnable is difficult to manage. There are still a lot of questions. After writing this framework, I realize that this requires a completely different solution. This makes me re-examine the problem, see how others solve similar problems, and study the Swing source code.