1.4. Threads are Everywhere(线程无处不在)
Even if your program never explicitly creates a thread, frameworks may create threads on your behalf, and code called from these threads must be thread-safe. This can place a significant design and implementation burden on developers, since developing thread-safe classes requires more care and analysis than developing non-thread-safe classes.
即使你的程序没有显式的创建线程,但是你所用的框架可能会为你创建线程,这就要求被这些线程调用的代码必须是线程安全的。这就要求开发者在设计和实现的时候要充分考虑和分析。
Every Java application uses threads. When the JVM starts, it creates threads for JVM housekeeping tasks (garbage collection, finalization) and a main thread for running the main method. The AWT (Abstract Window Toolkit) and Swing user interface frameworks create threads for managing user interface events. Timer creates threads for executing deferred tasks. Component frameworks, such as servlets and RMI create pools of threads and invoke component methods in these threads.
每一个java应用程序都会用到线程。当虚拟机开始启动的时候,虚拟机会为JVM启动一些内部事务管理的线程(垃圾回收、资源释放)还有一个main线程来执行main函数。AWT和Swing这样的用户界面框架会创建响应用户时间的线程。计时器会穿件执行延期任务的线程。组件框架,例如servlet和RMI,会创建线程池,并且会在这些线程中机会组件方法。
If you use these facilities as many developers do you have to be familiar with concurrency and thread safety, because these frameworks create threads and call your components from them. It would be nice to believe that concurrency is an "optional" or "advanced" language feature, but the reality is that nearly all Java applications are multithreaded and these frameworks do not insulate you from the need to properly coordinate access to application state.
如果像其他大多数程序员一样,想要使用这样的机制的话,你必须要熟悉并发机制和线程安全,因为这样的框架会创建线程,并从这些线程中调用你开发的组件。如果你依然认为并发是一种“可选的”和“高级的”语言特性,这或许不是完全错误的,但事实上几乎所有的java应用程序都是多线程的,而且所有的框架可能会要求你去恰当的处理应用的状态。
When concurrency is introduced into an application by a framework, it is usually impossible to restrict the concurrency-awareness to the framework code, because frameworks by their nature make callbacks to application components that in turn access application state. Similarly, the need for thread safety does not end with the components called by the framework it extends to all code paths that access the program state accessed by those components. Thus, the need for thread safety is contagious.
当并发被某个框架引入到应用系统中的时候,通常,如果要求框架本身具有并发控制本身是不太可能的。因为框架本身是通过回调应用程序的组件来访问程序的状态。同样的,线程安全不可能在被框架直接调用的组件中得到解决,它势必会扩展到那些访问程序状态的组件中。因此,线程安全的问题级联的。
Frameworks introduce concurrency into applications by calling application components from framework threads. Components invariably access application state, thus requiring that all code paths accessing that state be thread-safe.
框架通过在框架线程中调用应用组件来引入并发。而组件一定是用来访问程序状态的,这就要求所有访问程序状态的代码层次都必须是线程安全的。
The facilities described below all cause application code to be called from threads not managed by the application. While the need for thread safety may start with these facilities, it rarely ends there; instead, it ripples through the application.
下面描述的所有机制都会通过自身的线程(不是被应用程序所管理的)调用应用代码,当这些机制本身要求线程安全的时候,这就会要求这些应用代码本身也必须是线程安全的。
Timer. Timer is a convenience mechanism for scheduling tasks to run at a later time, either once or periodically. The introduction of a Timer can complicate an otherwise sequential program, because Timer Tasks are executed in a thread managed by the Timer, not the application. If a Timer Task accesses data that is also accessed by other application threads, then not only must the Timer Task do so in a thread-safe manner, but so must any other classes that access that data. Often the easiest way to achieve this is to ensure that objects accessed by the Timer Task are themselves thread-safe, thus encapsulating the thread safety within the shared objects.
Timer,Timer是可以非常便利的是一个任务在一段时间之后运行,或者周期式的运行。Timer会给顺序执行的程序带来某种复杂性,因为一个定时任务运行在一个由Timer管理的线程中,而不是应用程序管理的线程中。如果一个定时器需要访问会被其他应用线程访问的数据,这就必须要求定时器任务使用线程安全的访问方式,其他访问该数据的所有类必须使用线程安全的方式。达到这样效果的最简单的方式是首先保证被定时任务访问的类本身是线程安全的,然后把这种线程安全性封装到其他的共享对象中。
Servlets and JavaServer Pages (JSPs). The servlets framework is designed to handle all the infrastructure of deploying a web application and dispatching requests from remote HTTP clients. A request arriving at the server is dispatched, perhaps through a chain of filters, to the appropriate servlet or JSP. Each servlet represents a component of application logic, and in high-volume web sites, multiple clients may require the services of the same servlet at once. The servlets specification requires that a servlet be prepared to be called simultaneously from multiple threads. In other words, servlets need to be thread-safe.
Servlets和JSP。Servlet架构被设计成用来处理部署一个web应用和分发来自远程http客户端请求的所有基础工作。当一个到达server端的请求会被分发到恰当的servlet或者jsp中进行处理(可能会通过一条filter链)。每个servlet会代表着一个应用处理逻辑。在大型的web站点中,多个客户端可能会同时请求执行servlet的service方法。Servlet规范中要求一个servlet应该能够同时被多个线程执行,换句话说,servlet应该是线程安全的。
Even if you could guarantee that a servlet was only called from one thread at a time, you would still have to pay attention to thread safety when building a web application. Servlets often access state information shared with other servlets, such as application-scoped objects (those stored in the ServletContext) or session-scoped objects (those stored in the per-client HttpSession). When a servlet accesses objects shared across servlets or requests, it must coordinate access to these objects properly, since multiple requests could be accessing them simultaneously from separate threads. Servlets and JSPs, as well as servlet filters and objects stored in scoped containers like ServletContext and HttpSession, simply have to be thread-safe.
即使你能够保证一个servlet在同一个时间只能够被一个线程访问,当你建立web站点的时候,你还是应该注意线程安全问题。Servlet通常会访问被其他servlet共享的状态信息,例如Application作用域的对象(保存在servlet的ServletContext中)或者session作用域的对象(保存在每一个客户端的HttpSession中)。当一个servlet访问servlet和request作用域的共享对象的时候,必须要进行恰当的协调操作,这是因为多个请求可能从互相独立的线程中分别同时访问这些对象。Servlet和Jsp以及filter和保存在ServletContenxt和HttpSession中的对象都应该是线程安全的。
Remote Method Invocation. RMI lets you invoke methods on objects running in another JVM. When you call a remote method with RMI, the method arguments are packaged (marshaled) into a byte stream and shipped over the network to the remote JVM, where they are unpacked (unmarshaled) and passed to the remote method.
远程方法调用。远程方法调用可以让你调用在别的jvm中对象的方法。当使用远程方法调用去访问一个远程方法的时候,方法参数被封装成一个二进制流通过网络传送到远程jvm,在二进制流被远程jvm获取后,流会被解包,然后传送给远程方法。
When the RMI code calls your remote object, in what thread does that call happen? You don't know, but it's definitely not in a thread you created your object gets called in a thread managed by RMI. How many threads does RMI create? Could the same remote method on the same remote object be called simultaneously in multiple RMI threads?[4]
[4] Answer: yes, but it's not all that clear from the Javadoc you have to read the RMI spec.
那么,你知道当你的方法被远程调用的时候,这样的调用是在那个线程中发生的吗?你应该不会知道,因为调用不是发生在你创建的线程中,而是发生在被RMI管理的线程中。你知道RMI创建了几个线程吗?同一个远程对象中的相同方法可以被多个RMI线程同时调用吗?答案是肯定的,但这不是在java文档中说明的,如果你要了解这一点儿,你得去看RMI规范。
A remote object must guard against two thread safety hazards: properly coordinating access to state that may be shared with other objects, and properly coordinating access to the state of the remote object itself (since the same object may be called in multiple threads simultaneously). Like servlets, RMI objects should be prepared for multiple simultaneous calls and must provide their own thread safety.
远程对象必须主要以避免以下两项线程安全隐患。以恰当的方式访问与其他对象共享的状态。两外一个就是以恰当的方式访问有可能被自己改变的状态(这是因为相同的对象可能被多个线程同时访问)。这有点儿像servlet,所有的远程类应该准备好被多个线程同时调用,并且每个对象都要提供线程安全性。
Swing and AWT. GUI applications are inherently asynchronous. Users may select a menu item or press a button at any time, and they expect that the application will respond promptly even if it is in the middle of doing something else. Swing and AWT address this problem by creating a separate thread for handling user-initiated events and updating the graphical view presented to the user.
Swing和AWT架构。用户界面的应用程序在本质上都是异步的。用户有可能在任何时候选择一个下拉菜单或者按下一个按钮。这时候用户可能会期望应用程序立刻做出响应,他们不会去关心在这中间应用程序是否正在做其他的事情。Swing和Awt框架会通过创建一个独立线程的方式来应对该问题,独立线程的任务是处理用户触发的事件,并在图形界面上作出相应的响应。
Swing components, such as JTable, are not thread-safe. Instead, Swing programs achieve their thread safety by confining all access to GUI components to the event thread. If an application wants to manipulate the GUI from outside the event thread, it must cause the code that will manipulate the GUI to run in the event thread instead.
Swing组件,例如JTable,并不是线程安全的。Swing程序通过将所有对用户界面的方法限制在event线程来获取线程安全性。也就是说,如果一个应用程序的想要改变用户界面,它必须调用event线程中相应代码来修改。
When the user performs a UI action, an event handler is called in the event thread to perform whatever operation the user requested. If the handler needs to access application state that is also accessed from other threads (such as a document being edited), then the event handler, along with any other code that accesses that state, must do so in a thread-safe manner.
当用户执行任务一个UI动作的时候,事件处理线程中的一个事件句柄将会被调用来处理该请求。如果该句柄需要访问一个已经被其他线程占用的状态(例如文件正在被剪辑),那么时间处理句柄会被会放入处理队列,使用线程安全的方式访问。