站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > NetBeans API Javadoc 4.1.0

Threading in the Open APIs - NetBeans API Javadoc 4.1.0

Contents

Introduction

The Open APIs have a well-defined threading model, i.e. a set of conventions and requirements that should be used in regards to use of threads and long-running computations by all users of the Open APIs. This model applies both to API code itself; code in the IDE's private implementation; and all code present in modules.

Rather than attempt to make all calls in the APIs unconditionally thread-safe, which is not practical for the same reasons that much Java Platform code is not thread-safe in this way, the APIs are designed to have different threading requirements depending on which section of the APIs is being used. For the most part the division corresponds to API packages, although there are other considerations.

This document will begin by outlining several different domains, each of which has its own threading characteristics. Then, the APIs will be divided functionally into the different domains. Programmers who pay attention to this division and use the appropriate threading models throughout their code are of course not guaranteed freedom from deadlocks, UI freezes, race conditions, and multithreaded data structure corruption, but the risk of these types of errors is greatly reduced. Where appropriate, notes are included as to how extensively a domain has been tested for safety.

Threading-Model Domains

Thread-safe

Several areas of the Open APIs are designed to be completely thread-safe, which is to say that calls to these APIs may be made from any thread without concern for deadlocks or corruptions. Most importantly, the Filesystems API, Datasystems API, and Java Hierarchy API fall into this category. All code in these APIs is designed to expect concurrent access from multiple threads, and is automatically synchronized against such a possibility.

Implementations of abstract API objects in these packages should also follow this convention and use language-level synchronization where needed. Such implementations include custom filesystems, data loaders, data objects, cookie supports, hierarchy element implementations, etc. Note that AbstractFileSystem assumes much of the burden of synchronization for typical file system implementations, including the standard LocalFileSystem.

Swing abstract text documents are assumed to be thread-safe for callers, so implementors of editors must take care to synchronize these objects to prevent corruption. For atomicity of transactions on documents, callers should use NbDocument.runAtomic(...) or NbDocument.runAtomicAsUser(...) as needed. Note that this thread-safety does not extend to GUI components built using the documents, such as EditorSupport.Editor.

Note that these packages do not generally protect the programmer against delays, i.e. some calls may require a user-perceptible amount of time to complete. In such cases, GUI code should be careful when using the APIs, lest temporary screen freezes or jumpiness occur. That said, all calls in these packages which are expected to be slow provide a mechanism alleviating the problem:

Filesystems has been extensively tested for thread safety and appears to be quite robust. Datasystems has not undergone as extensive testing but still has no known threading problems. The implementation of thread-safe nodes is quite new as of this writing (build 383), but it is expected that the reimplementation will solve a number of previously intractable threading problems, as well as simplifying the caller-side semantics. Thread-safe documents are entirely up to the editor implementation, though there is associated API code such as updates Line.Sets which should be safe.

Unsynchronized

Some utility APIs are unsynchronized and are only suitable for access from within a single thread, as is common in utility classes such as the Java Collections API. Such APIs do not interact directly with other APIs and so this is reasonable. Such APIs include enumerations, safe I/O, and data transfer.

AWT Event Queue

A number of pieces of the API are designed to operate as GUI components; like in any AWT/Swing GUI application, care must be taken to only access these APIs within the AWT event queue, and not to consume an unreasonable amount of time or block when running code within them. Most importantly, the entire Explorer API is AWT-threaded, as well as the Window System API.

To make calls into these APIs from non-event-queue threads, the normal Swing mechanisms should be used, e.g. SwingUtilities.invokeLater(...).

Note that nodes are not accessed in the event queue, they are mutexed; however, Explorer views are (and must be, since they are visual components). To interface between these domains, the Visualizer class and some associated models such as NodeTreeModel are used by Explorer views to provide event-queue-safe calling conventions while being asynchronously updated from the node hierarchy. Other code which must run in the event queue could also use these classes to interact safely with the node hierarchy. The visualizer layer is expected to be removed in the future.

Startup

Certain code is run (in the NetBeans private implementation of the APIs) in the application main thread during IDE startup time. The main impact of this on module authors is that some code may be run in this main thread, specifically module install code. Generally such code should avoid waiting for the event queue (though non-Window-System GUI objects such as dialogs may be created at install or restore item, and Window System top components at install time only).

Module authors should be cautious about code run from ModuleInstall implementation methods, using only thread-safe calls or tasks.

TopComponent externalization methods may also be called in this main thread, during Window System startup, as well as potentially in the event queue, and thus should be as cautious.

Mutexes

Certain APIs are thread-safe, but for implementation purposes require internal synchronization, and this synchronization is also exposed to caller-side code which requires transactional operations; this is generally done using Mutexes.

Each mutex follows the usual model of permitting any number of simultaneous readers, or a single writer, to exist in the mutex block. (Also, re-entering a mutex with the same type of access is harmless, as is entering in read mode while already holding write mode. Trying to re-enter in write mode while holding a read mode lock will cause a quick deadlock; to catch these, run the IDE with the system property -Dnetbeans.debug.threads=true.) There are four calls which permit threads to access a mutex: Mutex.readAccess(Runnable), Mutex.readAccess(Mutex.Action), Mutex.writeAccess(Runnable), and Mutex.writeAccess(Mutex.Action). Collectively these provide not only read-versus-write access to the mutex, but also the ability to either run code asynchronously whenever the mutex is next available, versus running code with a return value and blocking for it. (Compare similar calls in EventQueue and SwingUtilities.)

Typically an API subsystem has an associated public mutex or mutexes; other mutexes may be constructed if needed. For example, the project system has a single public mutex.

There is a special pseudo-mutex which has an interesting implementation, namely to synchronize with the AWT event queue. Mutex.EVENT makes no distinction between read and write access, permitting only one thread at a time in each. Mutex.EVENT.readAccess(Runnable) (or Mutex.EVENT.writeAccess(Runnable)) has an effect equivalent to SwingUtilities.invokeLater, while Mutex.EVENT.readAccess(Mutex.Action) (or Mutex.EVENT.writeAccess(Mutex.Action)) is equivalent to SwingUtilities.invokeAndWait. However, if the calling thread is already in the event queue, this mutex executes the code immediately (rather than waiting for other pending events to be processed).

The advantage of Mutex.EVENT over directly calling EventQueue methods (besides the special behavior for code already in the event queue) is that it is possible to construct a large amount of code to expect some mutex, and then quickly switch between the event-queue mutex and some independent mutex. This was used to advantage when creating an independent mutex for node children (which previously had been an alias for the event-queue mutex)!

Engines

There are a couple places in the APIs where certain types of operations are clearly expected to take macroscopic time, if they terminate at all, and thus have a special isolated threading model. This applies mainly to running Ant. There are no special API considerations for this since Ant is normally single-threaded and does not normally call back to NetBeans code. Where this does happen (from a custom in-VM task), write code conservatively.

Tasks

For explicit control over the performance of miscellaneous long-running tasks, which require their own threads, the APIs provides the utility class Task which occurs throughout the APIs as the result of various asynchronous operations. In and of itself it does not do much interesting except provide thread-safe status information, an exit status, and the ability to fire termination events.

However, it is typically used in conjunction with a RequestProcessor which does provide a separate thread or thread group to contain the running tasks, as well as other functionality including task priorities, and delayed scheduling (with the option to cancel a pending job). Although callers can create their own request processors, typical usage is to use a single instance in the system with RequestProcessor.postRequest(Runnable); this ensures that all long-running code in the IDE (parsing, internal database updates, and so forth) is serialized and prioritized, rather than contending all at once for processor time and triggering virtual memory thrashes (as might result from the naive use of background threads).

It is also useful to post a request to the processor in the middle of GUI code, if the request might take a perceptible amount of time to complete.

Division of APIs into Domains

The Open APIs are divided into threading domains as follows:
Modules
Unsynchronized (though there is little implementation in this API). Most code to really handle modules is present in the private implementation, and thus has no documented model.
DialogDisplayer
Follows Swing Threading Model - e.g. it is generally safe to realize the components from non event queue thread, however any callback made then has to be handled in event queue only. DialogDisplayer.notify(...) and similar calls (customize and select nodes) use the event queue in GUI mode, however, and is assumed to block (so do not hold any locks when you call it, nor wait from event queue for result of such operation).
ErrorManager.notify(...)
Thread-safe.
Filesystems
Thread-safe.
Datasystems
Thread-safe. But org.openide.util.datatransfer is unsynchronized.
Explorer
Event queue only. Includes event-queue-safe interface to node hierarchy.
Nodes
Generally should all run in event queue, though this is not yet enforced.
Actions
Action.actionPerformed(ActionEvent) must be called from the event thread. The action may choose to do all of its work synchronously in this call, or if necessary do some work in another thread, at its sole discretion.
Window System
Event queue.
Options
Thread-safe.
Editor
Mostly thread-safe. Text documents should be locked using special locking provisions if desired. Document preparation may also be run as a task. Contains a default editor pane implementation which is displayed in the event queue, though document-related operations are still thread-safe.
Utility Classes
Variable: org.openide.awt should be used from the event queue; org.openide.util.Enumerations and org.openide.util.io are unsynchronized. org.openide.util is generally thread-safe.

Built on May 4 2005.  |  Portions Copyright 1997-2005 Sun Microsystems, Inc. All rights reserved.