SWT中初学者不经意会碰到这个异常,有时候很明确是创建多个Display对象引起的,但是为什么呢?
-
Q: Why do I get the error "org.eclipse.swt.SWTException: Invalid thread access"?
-
A: In SWT, by definition the thread that creates the Display is a UI-thread. This thread is responsible for reading and dispatching events from the operating system event queue, and invoking listeners in response to these events. Listener code is executed in the UI-thread. This makes an SWT application generally quite responsive, behaving like most other operating system programs. However, any long operation, when executed by a listener, will run in the UI-thread and prevent it from reading and dispatching events, thus hanging the application.
If a listener has a large amount of work to perform, instead of performing that work in the UI-thread, it can fork a separate thread so the UI-thread can continue dispatching events. If the other thread needs to execute code that accesses an SWT object, such as changing the string in a label, there is a concurrency issue. At the very least, some kind of synchronization is necessary to prevent the operating system or SWT from crashing, hanging or behaving unpredictably.
SWT implements a single-threaded UI model often called apartment threading. In this model, only the UI-thread can invoke UI operations. SWT strictly enforces this rule. If you try and access an SWT object from outside the UI-thread, you get the exception "org.eclipse.swt.SWTException: Invalid thread access". Different operating systems have different rules governing threads, UI components and synchronization. Some use a single-threaded UI model like SWT. Others allow only one thread at a time in the window system library, controlling access through a global lock. This type of multi-threaded UI model is often called free threading. Currently, in order to be simple, efficient and portable, SWT is apartment threaded.
To allow background threads to perform operations on objects belonging to the UI-thread, the methods syncExec(Runnable runnable) and asyncExec(Runnable runnable) of Display are used. These are the only methods in SWT that can be called from any thread. They allow a runnable to be executed by the UI-thread, either synchronously, causing the background thread to wait for the runnable to finish, or asynchronously allowing the background thread to continue execution without waiting for the result. A runnable that is executed using syncExec() most closely matches the equivalent direct call to the UI operation because a Java method call always waits for the result before proceeding, just like syncExec().
The following code sets the text of a label from a background thread and waits for the operation to complete:
display.syncExec( new Runnable() { public void run(){ label.setText(text); } });
但貌似new Display涉及不到多线程: Applications which are built with SWT will almost always require only a single display. In particular, some platforms which SWT supports will not allow more than one active display. In other words, some platforms do not support creating a new display if one already exists that has not been sent the dispose() message.
在建立swt的程序时一般总是只需要一个display对象。但也有特别的情况,有的平台不支持swt创建多个活动的display对象。也就是说,如果 已存在一个display对象,但是给它没有发送disose()消息来销毁它,有的平台是不支持再创建一个新的display对象。
SWT实现的是单线程UI模型,在这个模型里,只有UI线程(创建首个Display对象的线程)才能调用UI操作,如果从UI线程外访问SWT对象就会 得到异常"org.eclipse.swt.SWTException: Invalid thread access".
但我想重复new Display对象也未必和多线程挂得上勾,查看Display源码及一些资料,应该基于所创建新Display对象之线程已经存在的判断:
if (Displays [i].thread == thread) SWT.error (SWT.ERROR_THREAD_INVALID_ACCESS);
