这是否意味着两个线程不能同时更改底层数据?或者它是否意味着当多个线程执行给定的代码段时,该代码段将以可预测的结果运行?
当前回答
完成其他回答:
只有当方法中的代码做以下两件事之一时,同步才会令人担忧:
使用一些非线程安全的外部资源。 读取或更改持久对象或类字段
这意味着在方法中定义的变量总是线程安全的。对方法的每次调用都有自己版本的这些变量。如果方法是由另一个线程调用的,或者是由同一线程调用的,甚至是方法调用自身(递归),这些变量的值是不共享的。
线程调度不保证是循环的。一个任务可能会以牺牲相同优先级的线程为代价完全占用CPU。你可以使用Thread.yield()来获得良心。你可以使用(java) thread . setpriority (thread . norm_priority -1)来降低线程的优先级
另外还要注意:
迭代这些“线程安全”结构的应用程序的巨大运行时成本(已经被其他人提到)。 Thread.sleep(5000)应该休眠5秒。但是,如果有人更改了系统时间,您可能会睡很长时间或根本没有时间。操作系统记录唤醒时间是绝对的,而不是相对的。
其他回答
一个信息量更大的问题是,是什么使代码不线程安全——答案是,有四个条件必须成立……想象一下下面的代码(它是机器语言翻译)
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.
不要将线程安全性与决定论混淆。线程安全代码也可以是非确定性的。考虑到使用线程代码调试问题的难度,这可能是正常的情况。: -)
线程安全只是确保当一个线程修改或读取共享数据时,没有其他线程可以以改变数据的方式访问它。如果代码依赖于特定的执行顺序来确保正确性,那么除了线程安全所需的同步机制之外,还需要其他同步机制来确保这一点。
用最简单的话来说:P 如果在一个代码块上执行多个线程是安全的,那么它就是线程安全的*
*适用条件
条件被其他的答案提到,比如 1. 如果你执行一个线程或多个线程,结果应该是相同的。
正如其他人所指出的,线程安全意味着如果一段代码同时被多个线程使用,那么它将正常工作。
值得注意的是,这有时是有代价的,计算机时间和更复杂的编码,所以它并不总是可取的。如果一个类只能安全地在一个线程上使用,那么这样做可能会更好。
例如,Java有两个几乎相同的类:StringBuffer和StringBuilder。不同之处在于StringBuffer是线程安全的,因此StringBuffer的单个实例可以同时被多个线程使用。StringBuilder不是线程安全的,它被设计为仅由一个线程构建String的情况下(绝大多数情况下)的高性能替代品。
与其认为代码或类是线程安全的,我认为将操作视为线程安全的更有帮助。如果两个操作在从任意线程上下文运行时按照指定的方式运行,那么它们就是线程安全的。在许多情况下,类将以线程安全的方式支持一些操作组合,而其他则不支持。
例如,许多像数组列表和散列集这样的集合可以保证,如果它们最初只由一个线程访问,并且在引用对任何其他线程可见后永远不会被修改,那么它们可以被任何线程组合以任意方式读取而不受干扰。
More interestingly, some hash-set collections such as the original non-generic one in .NET, may offer a guarantee that as long as no item is ever removed, and provided that only one thread ever writes to them, any thread that tries to read the collection will behave as though accessing a collection where updates might be delayed and occur in arbitrary order, but which will otherwise behave normally. If thread #1 adds X and then Y, and thread #2 looks for and sees Y and then X, it would be possible for thread #2 to see that Y exists but X doesn't; whether or not such behavior is "thread-safe" would depend upon whether thread #2 is prepared to deal with that possibility.
As a final note, some classes--especially blocking communications libraries--may have a "close" or "Dispose" method which is thread-safe with respect to all other methods, but no other methods that are thread-safe with respect to each other. If a thread performs a blocking read request and a user of the program clicks "cancel", there would be no way for a close request to be issued by the thread that's attempting to perform the read. The close/dispose request, however, may asynchronously set a flag which will cause the read request to be canceled as soon as possible. Once close is performed on any thread, the object would become useless, and all attempts at future actions would fail immediately, but being able to asynchronously terminate any attempted I/O operations is better than require that the close request be synchronized with the read (since if the read blocks forever, the synchronization request would be likewise blocked).