这是否意味着两个线程不能同时更改底层数据?或者它是否意味着当多个线程执行给定的代码段时,该代码段将以可预测的结果运行?
当前回答
一个信息量更大的问题是,是什么使代码不线程安全——答案是,有四个条件必须成立……想象一下下面的代码(它是机器语言翻译)
totalRequests = totalRequests + 1
MOV EAX, [totalRequests] // load memory for tot Requests into register
INC EAX // update register
MOV [totalRequests], EAX // store updated value back to memory
The first condition is that there are memory locations that are accessible from more than one thread. Typically, these locations are global/static variables or are heap memory reachable from global/static variables. Each thread gets its own stack frame for function/method scoped local variables, so these local function/method variables, otoh, (which are on the stack) are accessible only from the one thread that owns that stack. The second condition is that there is a property (often called an invariant), which is associated with these shared memory locations, that must be true, or valid, for the program to function correctly. In the above example, the property is that “totalRequests must accurately represent the total number of times any thread has executed any part of the increment statement”. Typically, this invariant property needs to hold true (in this case, totalRequests must hold an accurate count) before an update occurs for the update to be correct. The third condition is that the invariant property does NOT hold during some part of the actual update. (It is transiently invalid or false during some portion of the processing). In this particular case, from the time totalRequests is fetched until the time the updated value is stored, totalRequests does not satisfy the invariant. The fourth and final condition that must occur for a race to happen (and for the code to therefore NOT be "thread-safe") is that another thread must be able to access the shared memory while the invariant is broken, thereby causing inconsistent or incorrect behavior.
其他回答
是也不是。
线程安全不仅仅是确保共享数据一次只能被一个线程访问。您必须确保对共享数据的顺序访问,同时避免竞争条件、死锁、活动锁和资源短缺。
当多个线程同时运行时,不可预知的结果并不是线程安全代码的必要条件,但这通常是一种副产品。例如,您可以使用一个共享队列、一个生产者线程和几个消费者线程来设置生产者-消费者方案,并且数据流可能完全可预测。如果你开始引入更多的消费者,你会看到更多随机的结果。
不要将线程安全性与决定论混淆。线程安全代码也可以是非确定性的。考虑到使用线程代码调试问题的难度,这可能是正常的情况。: -)
线程安全只是确保当一个线程修改或读取共享数据时,没有其他线程可以以改变数据的方式访问它。如果代码依赖于特定的执行顺序来确保正确性,那么除了线程安全所需的同步机制之外,还需要其他同步机制来确保这一点。
我喜欢Brian Goetz的Java并发实践中的定义,因为它的全面性
如果一个类在从多个线程访问时行为正确,那么它就是线程安全的,而不管运行时环境对这些线程的执行是如何调度或交错的,并且在调用代码方面没有额外的同步或其他协调。
一个更容易理解的方法是,是什么使代码不是线程安全的。有两个主要问题会使线程应用程序产生不需要的行为。
Accessing shared variable without locking This variable could be modified by another thread while executing the function. You want to prevent it with a locking mechanism to be sure of the behavior of your function. General rule of thumb is to keep the lock for the shortest time possible. Deadlock caused by mutual dependency on shared variable If you have two shared variable A and B. In one function, you lock A first then later you lock B. In another function, you start locking B and after a while, you lock A. This is a potential deadlock where first function will wait for B to be unlocked when second function will wait for A to be unlocked. This issue will probably not occur in your development environment and only from time to time. To avoid it, all locks must always be in the same order.
线程安全代码是指即使有多个线程同时执行也能正常工作的代码。
http://mindprod.com/jgloss/threadsafe.html