Final discussion of swing threads-asynchronous model

Source: Internet
Author: User
Introduction and review
Before studying the tree, table, and asynchronous model, I will first review swing's single-thread rule and check its meaning.
Swing's single-threaded rule means that a swing component can only be accessed by one thread at a certain time.This rule applies to gets and sets, and "one thread" usually refers to the event dispatching thread.
The single-thread rule is commensurate with the application of the UI component, because the UI components are often used in a single thread mode and most actions are initiated by the user. It is difficult and tedious to build a thread-safe component: it is best to avoid it. But apart from its advantages, single-threaded rules have more meanings.
All events of the swing component are sent and received in the event-Dispatch thread. In addition, they do not comply with the single-thread rules. For example, a property-change events event should be sent in the event dispatching thread, and a model-change events event should be received in the event dispatching thread.
For model-based components such as jtable and jtree, single-threaded rules mean that only the model itself can be accessed in the event dispatching thread. For this reason, the methods in the model must be executed quickly and cannot be blocked. Otherwise, the entire user interface will be unresponsive.
But imagine you have a jtree with treemodel to access a remote server?You will find that when the server is unavailable or too busy, your call to the treemodel will be blocked and the entire user interface will be frozen. What can you do to improve the interface response capability?
Suppose that you have a jtable with a tablemodel that manages a device on the network?You will find that the interface becomes dull when the managed devices are busy or the network is crowded. What should you do?
These difficulties occur when calling the thread. When requirements emerge, we still have several ways to use threads in swing, despite the existence of single-threaded rules.
This article shows two methods to use a thread-slow, remote, or other asynchronous model, and shows how to use it in the jtree and jtable components. (Not only the model accessed by the event dispatching thread is called the "Asynchronous" model .)

-----------------------------------------------------------------------------

Dynamic Tree

Suppose you have a jtree with a treemodel to access a remote server, but the server is slow or unreliable. What should you do?
Dynamictree demonstrates how to use background threads to dynamically expand jtree nodes.
Figure 1 shows the dynamictree that is being processed.

Figure 1

Split-Model Design

Dynamictree is based on a split-model design ). In this design, the real model is an asynchronous model, which may be slow or unreliable. The jtree model uses a standard Synchronization Model to maintain the snapshot of a real model. (The asynchronous model may be located on a remote server. For this reason, I usually call it a remote model, and I call the swing component model a local model .)
Using a local model to launch or cache remote models helps provide a swing component with high response capability that is reliable at all times. However, a disadvantage of this method is that model data must be repeated. Another problem is that two models are not always synchronized, and they have to be coordinated in some way.

Update upon expansion: Dynamic Browsing

A coordination method between models is to update the local model only when data is needed, rather than before. This technology is useful when the remote model is very slow or large, but this technology requires the model to be basically static. Dynamictree uses this method to browse a slow static tree model.
Dynamictree is an unexpanded root node at the beginning, and an expanded listener (expansion listener) is waiting for user input to expand the node. When the node is expanded, the listener starts a swingworker thread. The construct () method of this worker accesses the remote model and returns a new subnode. Then the worker finished () method is executed in the event dispatching thread and the subnode is added to the local model.
For simplicity, only one worker is allowed to run at the same time. If any node is collapsed, any worker in the current activity will be interrupted. (The program does not check whether the collapsed node is the ancestor of the worker node being expanded .)

Execution sequence

Figure 2 shows the node expansion process. The execution process starts and ends in the event dispatching thread on the left of the figure. The execution process of swingworker starts in the worker thread on the right of the figure. The solid arrow is a method call, the dotted arrow is a return, and the half arrow is an asynchronous request.

Figure 2 execution sequence of dynamictree
The rest of this section will discuss the class structure and implementation. You can skip the following remote model demo program. The "Download" section below explains how to download and run the demo program.

Implementation

Figure 3 is a rough class structure diagram.

Figure 3 dynamictree types
The local model is a defatretreemodel that contains the ultmutabletreenode node. Nodes must be variable to dynamically add subnodes. The userobject field of a mutable node can be used to point to a remote model.

Treenodefactory

The remote model implements the treenodefactory interface:

Public InterfaceTreenodefactory {
Defaultmutabletreenode [] createchildren (Object userobject)
ThrowsException;
}

Createchildren () is called in the worker thread. Similar to most asynchronous and remote methods, it throws an exception (usually a RemoteException or interruptedexception ). The userobject parameter is a link of the recently expanded node back to the remote model. Returning an array containing all subnodes at a time is more efficient than returning each subnode separately, and partial failure can be avoided.
During initialization, each subnode must be set with the allowschildren attribute and a link pointing back to the remote model. (If the remote node is a leaf node, the allowschildren attribute is set to false; otherwise, allowschildren is set to true, indicating that the node can be expanded .)
Defaulttreenodefactory is an adapter (see design patterns in gof). It matches any treemodel with the treenodefactory interface. The default tree model is used in the demo. Some annotated code in the main method demonstrates how to install a filesystemnodefactory. Slowtreenodefactory is a packaging class used for demonstration; it inserts random latency during running.

Future work

I have tried to keep dynamictree simple. The node has no content except the node name. If content needs to be filled in the node, it will be better if the content can be asynchronously loaded. For example, you may use a tree selection listener to initialize the loading process.
The remote table demo in the next section is more practical.

-------------------------------------------------------------------------------

Remote table

Suppose you have a jtable with tablemodel that manages a remote device, but the interface is slow as a turtle crawling when the device is busy. What should you do?
The following remote table demo program displays or modifies a remote table model using background threads and asynchronous callback operations (asynchronous callbacks.
Figure 4 is a remote table editor that is sending a new value to the server. Unprocessed cells are yellow before the update is complete.

Figure 4

Remotetable Bean

The remote table demo program is implemented using RMI. It consists of a server and two clients (one editor and one viewer. The viewer is actually an editor that disables editing.
The client uses a remotetable component bean, which is a subclass of jtable designed for remote table model collaboration. The editor shown in Figure 4 consists of a remotetable component, a status tag, a simple active metering device (lower right corner), and code used to locate the server.

Updated upon notification

Dynamictree updates its local model only when it needs data. The difference is that the remotetable component obtains all the data from the beginning and then listens for subsequent changes. The notification-based technology can work with dynamic models and work well when the models are relatively small.
Modify the drive notification for cells. After the user completes cell editing, remotetable marks the edited cells as unprocessed (highlighted in yellow) and schedules an swingworker task. The construct () method of the woker sends the new value to the remote model.
When the remote model receives a new value, it notifies the listener of changes. The only function of worker's finished () method is to confirm that the task has been completed. After the notifications from the remote model are accepted and processed, the yellow unprocessed cells are converted back to the normal cells.

Task queue

Remotetable uses a queuedexecutor to schedule its swingworker tasks. queuedexecutor executes all tasks in a single thread in sequence. (Queuedexecutor is part of Doug Lea's util. Concurrent package. For more information, see the references section .) The remote model uses RMI callback to notify its listener.
To support visual feedback, remotetable sends task events to the registered task listener. When a task is scheduled, the taskstarted () of the listener is called, and task-kended () is called when the task is completed. The client demo program uses these events to start or stop a small animation and update the status.

Execution Order

Figure 5 shows the cell update process. The start and end of the execution process are all the event dispatching threads on the left. The swingworker task is executed in the executor thread on the right. The execution process of worker's finished () method is not shown.

Figure 5 remotetable execution sequence

Simplified

For simplicity, the remote model does not provide protection for conflicting edits. So there is only one editor running at the same time. (Concurrent editing can be implemented by adding request IDs .)
Another simplified decision is that the client and server must negotiate the Column Structure of the table in advance. In other words, the server client provides row data, and the client must already know what table they want to process. In the demo, the client uses defamodelmodeltemplate to pre-define the names and classes of each column to determine which cell can be edited. (In the demo, the first two columns cannot be edited .)
The rest of this section describes the class structure and implementation. If you do not want to know the swingworker of the version used in this demo, you can skip. The "Download" section explains how to download and run this demo program.

Implementation

Figure 6 is a rough class structure diagram.

Figure 6 remotetable categories
The remote model implements the remotetablemodel interface, which is similar to the replicacttablemodel interface and throws an exception in all its methods. To start a client, the remote table model sends a full update event to the listener registered on the client.
Remotetablemodeladapter is attached with any tablemodel to a remotetablemodel. The table model in the demo is taken from the Java tutorial, but some latencies are inserted to simulate the actual situation. The remote table model event contains the value of the updated cell.
The remotetable component uses a defaultremotetablemodellistener to receive callbacks from remote models. This listener updates the local model in the event dispatching thread. Because the remote model may need to notify you to insert or delete certain rows, the listener requires that the local model support the insert and delete operations. The defaulttablemodel meets this requirement.

-------------------------------------------------------------------------------

Swingworker Revision

The demo program uses swingworker to perform time-consuming operations in the background, and then updates the UI.
The swingworker used in this demo is based on the swingworker class proposed in "using swingworker thread", but implements it again to correct a race condition and add timeout support, and improved exception handling.
This new implementation is also based on the futureresult class of Doug Lea's util. Concurrent package (see references ). The implementation of the swingworker class is simple and flexible because it relies heavily on the work done by futureresult.
The rest of this section describes the implementation details in more detail. Please continue or directly jump to the next step to download the source code.

Runnable futureresult

As its name implies, futureresult is used to maintain the result of an action. It is designed to be used together with a callable. callable is a runnable action that returns results:

Public InterfaceCallable {
Object call ()ThrowsException;
}

The new swingworker is a runnable futureresult. At runtime, it sets the result to the return value of construct (), and then calls the finished () method in the event dispatching thread. (Note: swingworker is an abstract class. You must subclass it and implement construct () and finished ().)
The following code comes from the run () method of swingworker:

Callable function =NewCallable (){
PublicObject call ()ThrowsException {
ReturnConstruct ();
}
};

Runnable dofinished =NewRunnable (){
Public VoidRun (){
Finished ();
}
};

Setter (function). Run ();
Swingutilities. invokelater (dofinished );

The first part converts construct () into a callable action, and the second part converts finished () to dofinished as runnable. Then, setter (function) is run and dofinished is called.

Setter (function)

The missing part is setter (function ). It creates a rigid runnable. During running, this runnable calls the function specified by the parameter and sets the return value for the result. The following code is from futureresult:

PublicRunnable setter (FinalCallable function ){
Return NewRunnable (){
Public VoidRun (){
Try{
Set (function. Call ());
}
Catch(Throwable ex ){
Setexception (Ex );
}
}
};
}

Pay attention to the try-Catch Block protection. If construct () throws anything (exception, error, etc.), it will be captured and recorded.

Don't rush: first construct, then start

Call start () to start the worker thread. This is an important difference between the swingworker of the revised version and the original version.
In the original version, the swingworker () constructor automatically starts the thread, which brings a risk of competition between the thread and the subclass constructor: When the swingworker () constructor has started the thread, the sub-class constructor is not complete yet. The remedy is to first construct swingworker and then call start ().
By the way, remotetable does not call start (). Correctly, swingworker is executed by queuedexecutor as a runnable.

Timeout support

The new swingworker supports timeout, which is achieved by overwriting the gettimeout () method and returning a non-zero value. When the timeout time is exceeded, the worker thread is interrupted.
To view the example of timeout, see the gettimeout () method in the Annotation Version and how to handle timeoutexception using dynamictree.
The timeout function is implemented using timedcallable, where the timedget () method of futureresult is used.

Enhanced Exception Handling

Anything thrown by the construct () method is recorded. In addition to the dead loop and deadlock, the new exception handling ensures that swingworker is in the "ready" state. That is to say, it either gets a correct result or an exception.
The following get () method is used to retrieve the result. This method is inherited from futureresult:

PublicObject get ()
ThrowsInvocationtargetexception, interruptedexception

If construct () throws an exception, the get () method throws the invocationtargetexception. To obtain the exception actually thrown by the construct () method, call gettargetexception ().
If the thread that gets the result is interrupted while waiting for the result, the get () method will throw interruptedexception -- but this situation is rare for swingworker, because the thread that gets the result is usually the event dispatching thread, and the result is always ready before finished () is called.

More call tools

The implementation of swingworker is in the jozart. swingutils package. In the same package, you can also find the invokeutils class, which also provides several invokexxx () methods. The background thread can use these methods to obtain the value and user input in the event dispatching thread, and then return the result to the background thread.

-------------------------------------------------------------------------------

Download

The source code of all the demo programs, as well as compiled class files and resources are included in this zip file: threads3 demos.zip
The threads3_demos.zip file contains the following content:
• Jozart/
O dynamictree/-dynamictree demo program.
O remotetable/-remotetable bean source code.
O remotetabledemo/-remotetable demo program.
O swingutils/-swingworker.
• Edu/-util. Concurrent package (only applicable classes ).
• Java. Policy-RMI security policy file (Security Policy ).
• Remotetable. jar-remotetable Bean (for various IDES)
Note: classes from util. Concurrent are selected from v1.2.3. Only the classes used in the demo program are included.
Note: To run these demo programs, Java 2 is required. (Collections is used in the util. Concurrent package .)
Run the demo program. First, decompress threads3_demos.zip to an empty folder. Do not change the name of the folder. Switch to the folder where you extract the file.

Run dynamictree

Run dynamictree as an applet:
> Appletviewer jozart/dynamictree/dynamictree.html
Run dynamictree as an application:
> JAVA jozart. dynamictree. dynamictree

Run remotetabledemo

The remote table server and client are designed to run separately with RMI. However, RMI may be difficult to set. RMI requires an Rmi Registry, an HTTP server, and a security policy file. Fortunately, another demo is not that troublesome. I will explain how to run it first.
Remotetabledemo packages an editor, a viewer, and a server.
Run remotetabledemo as an applet:
> Appletviewer jozart/remotetabledemo/remotetabledemo.html
Run remotetabledemo as application:
> JAVA jozart. remotetabledemo. remotetabledemo

Run servers and clients respectively

To run servers and clients separately, perform the following steps:
1. Confirm that rmiregistry is running:
> Start rmiregistry
2. confirm that an HTTP server is running:
> Start httpd
3. Switch to the demo program directory.
> Cd threads3
4. Start the server and client:
> JAVA-djava. Security. Policy = java. Policy
-Djava. RMI. server. codebase = http: // host/threads3/
Jozart. remotetabledemo. remotetableserver
> JAVA-djava. Security. Policy = java. Policy
-Djava. RMI. server. codebase = http: // host/threads3/
Jozart. remotetabledemo. remotetableeditor
> JAVA-djava. Security. Policy = java. Policy
-Djava. RMI. server. codebase = http: // host/threads3/
Jozart. remotetabledemo. remotetableviewer
You need to set your correct host name in codebase settings. You should also check that Java. Policy does not give too many permissions.
For more information about RMI, see the Java tutorial (see references ).

------------------------------------------------------------------------------

Conclusion

This article demonstrates two methods of using threads and model-based components (such as jtable and jtree) together. A revised swingworker tool class is provided.
At the beginning, I explained that it is difficult to build a thread-safe component. Java provides support for threads and locks at the language level, but this does not suddenly make concurrent programming easy. Swing's single-threaded Rule frees programmers from the complexity of thread security for their daily tasks, but it becomes a hindrance when they really need the thread.
To make it easier for me and the future users of my code to work on concurrent programming, I tried to use toolkit, proven effective design patterns, and widely accepted conventions.
At the end of the article, I would like to give a suggestion to anyone interested in swingworker extension or developing their own thread tools: find out your memory model.
For more information about resource sharing access control. Java synchronized statement ensures the security of passing values between threads. This important detail is often ignored by programmers. For more information about the Java memory model, see Doug Lea's online supplement to concurrent programming in Java. This reference and other reference links are listed in the next section.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.