Revie of SWING thread (on) SRX81 translation (Participation: 240, expert points: 1990) Published: 2003-11-5 11:22 new: 2003-11-6 9:31 am: 1.0 Read: 1775 Secondary
By Jonathan Simon10 / 24/2003 Original: http://today.java.net/pub/a/today/2003/10/24/swing.html? Page = 1 Incorrect SWING thread is slow, no response One of the main reasons for unstable Swing applications. 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 threads Solution Let us start with a most commonly used 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 The following is a modified method searchButton_actionPerformed: private void searchButton_actionPerformed () {outputTA.setText ( "Searching for:" searchTF.getText ()); // the String [] [] is used 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 ());}} .start () (); ""); For (int i = 0; i Below is a corrected code: private void search button_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 output (""); for (int i = 0; i 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 a site from it: 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 that it is How to solve those problems above. 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. Revie of Swing threads (below) SRX81 translation (Participate: 240, expert points: 1990) Published: 2003-11-6 9:10 am Version: 1.0 Read: 1740 times By Jonathan Simon10 / 24/2003 Original: http://today.java.net/pub/a/today.java.net/pub/A/TODAY/2003/10/24/swing.html? Page = 2 Previous: Reflections on Swing Threads (on Solution: Event Drive Programming All of these solutions have a common fatal defect - attempt to represent a task's function set while continuously changing threads. However, changing the thread requires an asynchronous model, while the thread processes Runnable asynchronously. Part of the problem is that we have a synchronized model at an attempt to achieve a synchronous model above an asynchronous thread model. This is the root of all runnails and dependence, execution order and internal SCoPing issues. If we can build a real asynchronous, we can solve our problems and greatly simplify the Swing thread. Before this, let's list the questions we have to solve: 1. Execute code in the appropriate thread 2. Use swingutilities.invokelater () asynchronously. Asynchronous execution results in the following problem: 1. Mutual coupling Component 2. Difficulties in variables 3. The order of execution allows us to consider messaging-based systems like Java Message Services (JMS) because they provide loose coupling between functional components in asynchronous environments. The message system triggers an asynchronous event, as described in Enterprise Integration Patterns. Interested participants listen to the event and respond to the event - usually by executing some of their own code. The result is a group of modular, loosely coupled components, and components can be added or removed from the system without affecting other components. More importantly, the dependence between components is minimized, and each component is a good definition and package - each of them is only responsible for their work. They simply trigger the message, and other components will respond to this message and respond to messages triggered by other components. Now, we first ignore the thread problem and decouple the components and transplanted into the asynchronous environment. After we solve the asynchronous problem, we will go back to see thread problems. As we have to see, it will be very easy to solve this problem. Let us take the examples introduced in front and transplant it to an event-based model. First, we call the lookup to a class called lookupManager. This will allow us to remove database logic in all UI classes and ultimately allow us to completely disengage the two. Below is the code of the LookupManager class: class lookupmanager {private string [] lookup (string text) {string [] results = ... // database lookup code return results} Now we start to convert to the asynchronous model. In order to call this call asynchronously, we need to return to the abstract call. In other words, the method cannot return any value. We will have something you want to know in terms of distinctive action. The most obvious event in our example is the search end event. So let's create a listener interface to respond to these events. This interface contains a single method lookupCompleted (). Below is the definition of the interface: Interface looklistener {public void lookCompleted (Iterator Results); This will allow us to pass other information without changing the LookUplistener interface. For example, we can include the strings and results that are found in the Lookupevent. The following is LookupEvent class: public class LookupEvent {String searchText; String [] results; public LookupEvent (String searchText) {this.searchText = searchText;} public LookupEvent (String searchText, String [] results) {this.searchText = searchText; this .results = results;} public string getsearchText () {return searchtext;} public string [] getResults () {return results;}} Note that the Lookupevent class is not variable. This is very important because we don't know who will deal with these events during the pass. Unless we created an event's protection copy to each listener, we need to make events unstrenomed. If not, a listener may unintentionally or maliciously revisit the event object and destroy the system. Now we need to call the LookUpComplete () event on lookupManager. We start by adding a set of LookupListener on LookupManager: List listeners = new ArrayList (); and a method LookupListener added and removed on the LookupManager: public void addLookupListener (LookupListener listener) {listeners.add (listener);} public void RemoveluggUplistener (LookUplistener Listener) {listener.Remove (Listener);} We need to call the listener's code when the action occurs. In our example, we will trigger a lookupCompleted () event while looking back. This means it iterations on the listener collection and uses a lookupCompleted () method using a lookupevent event object. I like to extract these code to a separate method Fire [event-method-name], which constructs an event object, iterations on the listener collection, and calls the appropriate method on each listener. This helps isolating the code of the primary logic code and calling the listener. Here is our fireLookupCompleted method: private void fireLookupCompleted (String searchText, String [] results) {LookupEvent event = new LookupEvent (searchText, results); Iterator iter = new ArrayList (listeners) .iterator (); while (iter.hasNext ( )) {Lookuplistener Listener = (lookuplistener) iter.next (); listener.lookUpCompleted (Event);}} The second line of code creates a new collection that is incorporated into the original audio collection. This decides on the LookupManager after the listener response event. If we are not a secure copy collection, a bored error occurred while some listeners should be called without being called. Below, we will call the FirelookUpCompleted auxiliary method when the action is completed. This is the end of the LOOKUP method's return query results. So we can change the Lookup method to trigger an event rather than return a string array itself. Below is a new lookup method: public void lookup (string text) {// mimic the server call delay ... try {thread.sleep (5000);} catch (exception e) {E.PrintStackTrace ();} // Imagine We got this from a server string [] results = new string [] {"book one", "book two", "book three"}; firelookupCompleted (text, result);} Now let us add a listener to lookupManager . We want to update the text area when you look back. In the past, we just call the setText () method directly. Because the text area is executed in the UI together with the database call. Since we have abstracted from the UI, we will use the UI class as a listener to the LookusManager, listen to the Lookup event and update yourself accordingly. First, we will achieve the listener interface class definition: public class FixedFrame implements LookupListener Then we implement interface methods: public void lookupCompleted (final LookupEvent e) {outputTA.setText ( ""); String [] results = e.getResults () ; For (int i = 0; i In order to demonstrate how to add new events, let's add an event starting with a lookup. We can add an event called lookupstarted () to lookuplistener, and we will trigger it before the lookup starts execution. We also create a FirelookupStarted () event call all lookuplistener's lookstarted (). The LOOKUP method is now as follows: Public void lookup (String text) {FirelookupStarted (text); // mimic the server call delay ... try {thread.sleep (5000);} catch (exception e) {E.PrintStackTrace (); } // imagine we got this from a server string [] results = new string [] {"book one", "book two", "book three"}; firelookupCompleted (text, result);} We also add new triggers Method FirelookupStarted (). This method is equivalent to the FirelookUpCompleted () method, except that we call the lookupstarted () method on the listener, and the event does not contain result sets. Here is the code: private void fireLookupStarted (String searchText) {LookupEvent event = new LookupEvent (searchText); Iterator iter = new ArrayList (listeners) .iterator (); while (iter.hasNext ()) {LookupListener listener = (LookupListener) iter .next (); listener.lookupstarted (Event);}} Last, we implements a lookstarted () method on the UI class, setting the text area prompt the current search. Public Void LookupStarted (Final Lookupevent E) {Outputta.Settext ("Searching for:" E.GetSearchText ());} This example shows how easy it is to add new events. Now let's take a look at the flexibility of displaying event-driven decoupling. We will demonstrate by creating a log class while outputting information in the command line when a search start and end. We call this class as Logger. The following is its code: public class Logger implements LookupListener {public void lookupStarted (LookupEvent e) {System.out.println ( "Lookup started:" e.getSearchText ());} public void lookupCompleted (LookupEvent e) {System. Out.println ("Lookup Completed:" " " E.GETRESULTS ());}} Now we add Logger as a listener for the LookusManager in the FixedFrame constructor. public FixedFrame () {lookupManager = new LookupManager (); lookupManager.addListener (this); lookupManager.addListener (new Logger ()); initComponents (); layoutComponents ();} Now that you've seen to add new events, create new Listener - show you the flexibility and scalability of the event driver. You will find that as you have more developed a program, you will be more skilled in your application to create a universal action. Like all other things, this requires only time and experience. It seems that many studies have been made on the event model, but you still need to compare it with other alternatives. Consider development time costs; the most important, this is a one-time cost. Once you have created a listener model and their actions, add a listener to your app in your app. Threads to now, we have solved the above asynchronous problems; through the listener to deliver the components, pass the variables through the event object, the order of execution by the event generation and the synonym registration. Let us return to thread problems, because it is brought here. It is actually very easy: Because we already have an asynchronous listener, we can simply let the listener decide which thread should be executed in. Consider the separation of the UI class and the LookUpManager. The UI class is based on an event and decides what processing needs. And, this class is also swing, and the log class is not. So let the UI class be responsible for determining what it should perform in what thread will make more meaningful. So let's take a look at the UI class again. Below is a LOOKUPCOMPLETED () method without a thread: public lookuote e) {outputta.setText (""); string [] results = E.GETRESULTS (); for (int i = 0; i