这是否意味着两个线程不能同时更改底层数据?或者它是否意味着当多个线程执行给定的代码段时,该代码段将以可预测的结果运行?


当前回答

是也不是。

线程安全不仅仅是确保共享数据一次只能被一个线程访问。您必须确保对共享数据的顺序访问,同时避免竞争条件、死锁、活动锁和资源短缺。

当多个线程同时运行时,不可预知的结果并不是线程安全代码的必要条件,但这通常是一种副产品。例如,您可以使用一个共享队列、一个生产者线程和几个消费者线程来设置生产者-消费者方案,并且数据流可能完全可预测。如果你开始引入更多的消费者,你会看到更多随机的结果。

其他回答

不要将线程安全性与决定论混淆。线程安全代码也可以是非确定性的。考虑到使用线程代码调试问题的难度,这可能是正常的情况。: -)

线程安全只是确保当一个线程修改或读取共享数据时,没有其他线程可以以改变数据的方式访问它。如果代码依赖于特定的执行顺序来确保正确性,那么除了线程安全所需的同步机制之外,还需要其他同步机制来确保这一点。

至少在c++中,我认为线程安全有点用词不当,因为它在名称中遗漏了很多内容。要实现线程安全,代码通常必须具有前瞻性。这通常不是一种被动的品质。

For a class to be thread-safe, it has to have "extra" features that add overhead. These features are part of the implementation of the class and generally speaking, hidden from the interface. That is, different threads can access any of the class' members without ever having to worry about conflicting with a concurrent access by a different thread AND can do so in a very lazy manner, using some plain old regular human coding style, without having to do all that crazy synchronization stuff that is already rolled into the guts of the code being called.

这就是为什么有些人更喜欢使用内部同步这个术语。

术语集

我遇到的这些概念主要有三组术语。第一个,历史上更受欢迎(但最糟糕)的是:

线程安全的 不线程安全

第二个(更好的)是:

线程的证据 线程兼容 线程敌意

第三个(甚至更好)是:

内部同步 外部同步 unsynchronizable

类比

线程安全~线程证明~内部同步

An example of an internally synchronized (aka. thread-safe or thread proof) system is a restaurant where a host greets you at the door, and disallows you from queueing yourself. The host is part of the mechanism of the restaurant for dealing with multiple customers, and can use some rather tricky tricks for optimizing the seating of waiting customers, like taking the size of their party into account, or how much time they look like they have, or even taking reservations over the phone. The restaurant is internally synchronized because all of this is included "behind the scenes" when you interact with it. You, the customer, don't do any of it. The host does all of it for you.

不是线程安全的(但是很好)~线程兼容~外部同步~自由线程

假设你去银行。有一条线,即争夺银行出纳员。因为你不是野蛮人,所以你知道在争夺资源的过程中,最好的办法就是像文明人一样排队。严格来说没人逼你这么做。我们希望你有必要的社会规划来自己做这件事。从这个意义上说,银行大厅在外部是同步的。

我们是否可以说它是线程不安全的?如果使用线程安全的、线程不安全的双极术语集,就会出现这种情况。这不是一个很好的术语集合。更好的术语是外部同步。银行大厅对多个客户的访问没有敌意,但它也不做同步他们的工作。这些都是客户自己做的。

This is also called "free threaded," where "free" is as in "free from lice"--or in this case, locks. Well, more accurately, synchronization primitives. That doesn't mean the code can run on multiple threads without those primitives. It just means it doesn't come with them already installed and it's up to you, the user of the code, to install them yourself however you see fit. Installing your own synchronization primitives can be difficult and requires thinking hard about the code, but also can lead to the fastest possible program by allowing you to customize how the program executes on today's hyperthreaded CPUs.

不线程安全(和坏)~线程敌对~不可同步

An example everyday analogy of a thread-hostile system is some jerk with a sports car refusing to use their blinkers and changing lanes willy-nilly. Their driving style is thread hostile or unsychronizable because you have no way to coordinate with them, and this can lead to contention for the same lane, without resolution, and thus an accident as two cars attempt to occupy the same space, without any protocol to prevent this. This pattern can also be thought of more broadly as anti-social, though that's less specific to threads and more generally applicable to many areas of programming.

为什么线程安全/不线程安全是一个糟糕的术语集

The first and oldest terminology set fails to make the finer distinction between thread hostility and thread compatibility. Thread compatibility is more passive than so-called thread safety, but that doesn't mean the called code is unsafe for concurrent thread use. It just means it's passive about the synchronization that would allow this, putting it off to the calling code, instead of providing it as part of its internal implementation. Thread compatible is how code should probably be written by default in most cases but this is also sadly often erroneously thought of as thread unsafe, as if it's inherently anti safety, which is a major point of confusion for programmers.

NOTE: Many software manuals actually use the term "thread-safe" to refer to "thread-compatible," adding even more confusion to what was already a mess! I avoid the term "thread-safe" and "thread-unsafe" at all costs for this very reason, as some sources will call something "thread-safe" while others will call it "thread-unsafe" because they can't agree on whether you have to meet some extra standards for safety (pre-installed synchronization primitives), or just NOT be hostile to be considered "safe". So avoid those terms and use the smarter terms instead, to avoid dangerous miscommunications with other engineers.

提醒我们的目标

本质上,我们的目标是颠覆混乱。

We do that by creating semi-deterministic systems we can rely on. Determinism is expensive, mostly due to the opportunity costs of losing parallelism, pipelining, and reordering. We try to minimize the amount of determinism we need to keep our costs low, while also avoiding making decisions that will further erode what little determinism we can afford. Thus, the semi- prefix. We just want certain little bits of our code's state to be deterministic, while the computational machinery underneath doesn't have to be completely so. Synchronization of threads is about increasing the order and decreasing the chaos in a multi-threaded system because having multiple threads leads to a greater amount of non-determinism naturally which must be subdued somehow.

总而言之,一些代码主体可以在“杂耍刀”上投入三种主要程度的努力。在多线程上下文中正确地工作。

The highest degree (thread-proof, etc.) means that a system behaves in a predictable manner even if you call it from multiple threads sloppily. It does the work necessary to achieve this itself so you don't have to. It makes this nice interface for you, the programmer writing calling code, so that you can pretend to live in a world without synchronization primitives. Because it's already included them internally. It's also expensive and slow and also somewhat unpredictable when it comes to how long it takes for tasks to complete due to synchronization it's doing, which must always be greater than the amount you need for your specific program because it doesn't know what your code will do. Great for casual coders who code in various scripting languages to do science or something, but aren't themselves writing highly efficient close-to-the-metal code. They don't need to juggle knives.

第二级(线程兼容等)意味着系统运行良好,调用代码能够可靠地及时检测到不可预测性,并在运行时使用自己安装的同步原语正确地处理它。D-I-Y同步。BYOSP =自带同步原语。至少你知道你调用的代码会很好地处理它们。这是为接近金属的专业程序员准备的。

The third degree (thread-hostile, etc.) means that the system doesn't behave well enough to play with anyone else and can only EVER be run single-threaded without incurring chaos. This is classic early 90s and earlier code, essentially. It was programmed with a lack of awareness about how it might be called or used from multiple threads to such a high degree that even if you try to add those synchronization primitives yourself, it just won't work because it makes old fashioned assumptions that these days seem anti-social and unprofessional.

However, some code only really makes sense called single-threaded and so is still written to be called that way intentionally. This is true especially for software that already has an efficient pipeline and memory access sequence, and doesn't benefit from the main purpose of multi-threading: hiding memory access latencies. Accessing non-cache memory is ridiculously slower than most other instructions. So whenever an application is waiting for some bit of memory access, it should switch to another task thread in the meantime to keep the processor working. Of course, these days, that could mean switching to another coroutine/fiber/etc. within the same thread, when available, because these are much more efficient than a thread context switch. But once even those are exhausted for the time being, it's time to switch threads executing on our core.

但有时,你已经很好地打包和排序了所有的内存访问,你最不想做的就是切换到另一个线程,因为你已经流水线化了你的代码来尽可能有效地处理这个问题。那么,丝线不会带来伤害。这是一个例子,但还有其他例子。

一般来说,我认为在编程要调用的代码时,尽可能地实现线程兼容是有意义的,特别是如果没有真正的理由不这样做,并且它只需要你在编码时意识到这一点。

我喜欢Brian Goetz的Java并发实践中的定义,因为它的全面性

如果一个类在从多个线程访问时行为正确,那么它就是线程安全的,而不管运行时环境对这些线程的执行是如何调度或交错的,并且在调用代码方面没有额外的同步或其他协调。

用最简单的话来说:P 如果在一个代码块上执行多个线程是安全的,那么它就是线程安全的*

*适用条件

条件被其他的答案提到,比如 1. 如果你执行一个线程或多个线程,结果应该是相同的。

完成其他回答:

只有当方法中的代码做以下两件事之一时,同步才会令人担忧:

使用一些非线程安全的外部资源。 读取或更改持久对象或类字段

这意味着在方法中定义的变量总是线程安全的。对方法的每次调用都有自己版本的这些变量。如果方法是由另一个线程调用的,或者是由同一线程调用的,甚至是方法调用自身(递归),这些变量的值是不共享的。

线程调度不保证是循环的。一个任务可能会以牺牲相同优先级的线程为代价完全占用CPU。你可以使用Thread.yield()来获得良心。你可以使用(java) thread . setpriority (thread . norm_priority -1)来降低线程的优先级

另外还要注意:

迭代这些“线程安全”结构的应用程序的巨大运行时成本(已经被其他人提到)。 Thread.sleep(5000)应该休眠5秒。但是,如果有人更改了系统时间,您可能会睡很长时间或根本没有时间。操作系统记录唤醒时间是绝对的,而不是相对的。