Does it mean that two threads can't change the underlying data simultaneously? Or does it mean that the given code segment will run with predictable results when more than one thread are running it?
这是否意味着两个线程不能同时更改基础数据?或者它是否意味着当多个线程运行时,给定的代码段将以可预测的结果运行?
15 个解决方案
#1
216
From Wikipedia:
Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads. In particular, it must satisfy the need for multiple threads to access the same shared data, and the need for a shared piece of data to be accessed by only one thread at any given time.
线程安全是适用于多线程程序的计算机编程概念。如果一段代码在多个线程同时执行期间正常运行,则它是线程安全的。特别是,它必须满足多线程访问相同共享数据的需要,并且在任何给定时间只需要一个线程访问共享数据块。
There are a few ways to achieve thread safety:
有几种方法可以实现线程安全:
Re-entrancy:
Writing code in such a way that it can be partially executed by one task, reentered by another task, and then resumed from the original task. This requires the saving of state information in variables local to each task, usually on its stack, instead of in static or global variables.
以这样的方式编写代码,使其可以由一个任务部分执行,由另一个任务重新进入,然后从原始任务恢复。这需要将状态信息保存在每个任务的本地变量中,通常是在其堆栈上,而不是静态或全局变量中。
Mutual exclusion:
Access to shared data is serialized using mechanisms that ensure only one thread reads or writes the shared data at any time. Great care is required if a piece of code accesses multiple shared pieces of data—problems include race conditions, deadlocks, livelocks, starvation, and various other ills enumerated in many operating systems textbooks.
使用确保只有一个线程可以随时读取或写入共享数据的机制来序列化对共享数据的访问。如果一段代码访问多个共享数据,则需要非常小心 - 问题包括竞争条件,死锁,活锁,饥饿以及许多操作系统教科书中列举的各种其他问题。
Thread-local storage:
Variables are localized so that each thread has its own private copy. These variables retain their values across subroutine and other code boundaries, and are thread-safe since they are local to each thread, even though the code which accesses them might be reentrant.
变量已本地化,因此每个线程都有自己的私有副本。这些变量在子例程和其他代码边界中保留其值,并且是线程安全的,因为它们对于每个线程是本地的,即使访问它们的代码可能是可重入的。
Atomic operations:
Shared data are accessed by using atomic operations which cannot be interrupted by other threads. This usually requires using special machine language instructions, which might be available in a runtime library. Since the operations are atomic, the shared data are always kept in a valid state, no matter what other threads access it. Atomic operations form the basis of many thread locking mechanisms.
通过使用不能被其他线程中断的原子操作来访问共享数据。这通常需要使用特殊的机器语言指令,这些指令可能在运行时库中可用。由于操作是原子操作,因此无论其他线程访问它,共享数据始终保持有效状态。原子操作构成了许多线程锁定机制的基础。
read more:
http://en.wikipedia.org/wiki/Thread_safety
-
in German: http://de.wikipedia.org/wiki/Threadsicherheit
用德语:http://de.wikipedia.org/wiki/Threadsicherheit
-
in French: http://fr.wikipedia.org/wiki/Threadsafe (very short)
法语:http://fr.wikipedia.org/wiki/Threadsafe(很短)
#2
67
Thread-safe code is code that will work even if many Threads are executing it simultaneously.
线程安全代码是即使许多线程同时执行它也能工作的代码。
#3
42
A more informative question is what makes code not thread safe- and the answer is that there are four conditions that must be true... Imagine the following code (and it's machine language translation)
一个更具信息性的问题是什么使代码不是线程安全的 - 答案是有四个条件必须是真的...想象下面的代码(和它的机器语言翻译)
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 it's 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.
第一个条件是存在可从多个线程访问的内存位置。通常,这些位置是全局/静态变量,或者是可从全局/静态变量访问的堆内存。每个线程为函数/方法范围的局部变量获取它自己的堆栈帧,因此这些本地函数/方法变量otoh(它们在堆栈上)只能从拥有该堆栈的一个线程访问。
第二个条件是有一个属性(通常称为不变量),它与这些共享内存位置相关联,必须为true或有效,才能使程序正常运行。在上面的示例中,属性是“totalRequests必须准确表示任何线程执行增量语句的任何部分的总次数”。通常,在更新发生更新之前,此不变属性需要保持为true(在这种情况下,totalRequests必须保持准确的计数)才能使更新正确。
第三个条件是在实际更新的某些部分期间不保留不变属性。 (在处理的某些部分,它暂时无效或错误)。在这种特定情况下,从获取totalRequests的时间到存储更新值的时间,totalRequests不满足不变量。
竞争发生必须发生的第四个也是最后一个条件(并且代码因此不是“线程安全的”)是另一个线程必须能够在不变量被破坏时访问共享内存,从而导致不一致或行为不正确。
#4
32
I like the definition from Brian Goetz's Java Concurrency in Practice for its comprehensiveness
我喜欢Brian Goetz的Java Concurrency in Practice中的定义,因为它的全面性
"A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code."
“如果一个类在从多个线程访问时行为正确,则无论运行时环境对这些线程的执行进行调度或交错,并且调用代码没有额外的同步或其他协调,它都是线程安全的。 “
#5
23
As others have pointed out, thread safety means that a piece of code will work without errors if it's used by more than one thread at once.
正如其他人所指出的那样,线程安全意味着如果一个代码一次被多个线程使用,那么一段代码就可以正常工作。
It's worth being aware that this sometimes comes at a cost, of computer time and more complex coding, so it isn't always desirable. If a class can be safely used on only one thread, it may be better to do so.
值得注意的是,这有时会带来成本,计算机时间和更复杂的编码,因此并不总是令人满意。如果一个类只能在一个线程上安全使用,那么这样做可能会更好。
For example, Java has two classes that are almost equivalent, StringBuffer
and StringBuilder
. The difference is that StringBuffer
is thread-safe, so a single instance of a StringBuffer
may be used by multiple threads at once. StringBuilder
is not thread-safe, and is designed as a higher-performance replacement for those cases (the vast majority) when the String is built by only one thread.
例如,Java有两个几乎相同的类,StringBuffer和StringBuilder。区别在于StringBuffer是线程安全的,因此一次可以由多个线程使用StringBuffer的单个实例。 StringBuilder不是线程安全的,并且当String仅由一个线程构建时,它被设计为更高性能的替代品(大多数情况下)。
#6
22
Thread-safe-code works as specified, even when entered simultaneously by different threads. This often means, that internal data-structures or operations that should run uninterrupted are protected against different modifications at the same time.
线程安全代码按指定的方式工作,即使由不同的线程同时输入也是如此。这通常意味着,应该不间断运行的内部数据结构或操作可以同时受到不同修改的保护。
#7
18
An easier way to understand it, is what make code not thread-safe. There's two main issue that will make a threaded application to have unwanted behavior.
理解它的一种更简单的方法是使代码不是线程安全的。有两个主要问题会导致线程应用程序出现不需要的行为。
-
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.由共享变量的相互依赖引起的死锁如果你有两个共享变量A和B.在一个函数中,你先锁定A然后再锁定B.在另一个函数中,你开始锁定B,一段时间后,你锁定A.是一个潜在的死锁,当第二个函数等待A解锁时,第一个函数将等待B解锁。此问题可能不会在您的开发环境中发生,并且只会不时发生。为避免这种情况,所有锁必须始终处于相同的顺序。
#8
9
Yes and no.
是的,不是。
Thread safety is a little bit more than just making sure your shared data is accessed by only one thread at a time. You have to ensure sequential access to shared data, while at the same time avoiding race conditions, deadlocks, livelocks, and resource starvation.
线程安全不仅仅是确保您的共享数据一次只能被一个线程访问。您必须确保对共享数据的顺序访问,同时避免竞争条件,死锁,活锁和资源不足。
Unpredictable results when multiple threads are running is not a required condition of thread-safe code, but it is often a by-product. For example, you could have a producer-consumer scheme set up with a shared queue, one producer thread, and few consumer threads, and the data flow might be perfectly predictable. If you start to introduce more consumers you'll see more random looking results.
运行多个线程时不可预测的结果不是线程安全代码的必需条件,但它通常是副产品。例如,您可以使用共享队列,一个生产者线程和少数消费者线程设置生产者 - 消费者方案,并且数据流可能是完全可预测的。如果你开始引入更多的消费者,你会看到更多随机的结果。
#9
8
In essence, many things can go wrong in a multi threaded environment (instructions reordering, partially constructed objects, same variable having different values in different threads because of caching at the CPU level etc.).
本质上,在多线程环境中可能出现许多问题(指令重新排序,部分构造的对象,由于CPU级别的缓存等,在不同线程中具有不同值的相同变量等)。
I like the definition given by Java Concurrency in Practice:
我喜欢Java Concurrency in Practice提供的定义:
A [portion of code] is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.
如果代码的一部分在从多个线程访问时行为正确,则它是线程安全的,无论运行时环境是否调度或交错执行这些线程,并且没有额外的同步或其他协调部分。调用代码。
By correctly they mean that the program behaves in compliance with its specifications.
通过正确,它们意味着程序的行为符合其规范。
Contrived example
Imagine that you implement a counter. You could say that it behaves correctly if:
想象一下,你实现了一个计数器。如果出现以下情况,您可以说它的行为正确:
-
counter.next()
never returns a value that has already been returned before (we assume no overflow etc. for simplicity) - all values from 0 to the current value have been returned at some stage (no value is skipped)
counter.next()永远不会返回之前已经返回的值(为简单起见,我们假设没有溢出等)
在某个阶段已返回从0到当前值的所有值(不跳过任何值)
A thread safe counter would behave according to those rules regardless of how many threads access it concurrently (which would typically not be the case of a naive implementation).
线程安全计数器将根据这些规则运行,无论有多少线程同时访问它(通常不是天真实现的情况)。
Note: cross-post on Programmers
注意:在程序员上交叉发布
#10
7
Simply - code will run fine if many threads are executing this code at the same time.
简单 - 如果许多线程同时执行此代码,代码将运行正常。
#11
4
Don't confuse thread safety with determinism. Thread-safe code can also be non-deterministic. Given the difficulty of debugging problems with threaded code, this is probably the normal case. :-)
不要将线程安全与确定性混淆。线程安全代码也可以是非确定性的。鉴于使用线程代码调试问题很困难,这可能是正常情况。 :-)
Thread safety simply ensures that when a thread is modifying or reading shared data, no other thread can access it in a way that changes the data. If your code depends on a certain order for execution for correctness, then you need other synchronization mechanisms beyond those required for thread safety to ensure this.
线程安全只是确保当线程正在修改或读取共享数据时,没有其他线程可以以更改数据的方式访问它。如果您的代码依赖于某个执行顺序以确保正确性,那么您需要除线程安全性所需的其他同步机制以确保这一点。
#12
3
To complete other answers:
要完成其他答案:
Synchronization is only a worry when the code in your method does one of two things:
当方法中的代码执行以下两项操作之一时,同步只是一种担忧:
- works with some outside resource that isn't thread safe.
- Reads or changes a persistent object or class field
适用于一些非线程安全的外部资源。
读取或更改持久对象或类字段
This means that variables defined WITHIN your method are always threadsafe. Every call to a method has its own version of these variables. If the method is called by another thread, or by the same thread, or even if the method calls itself (recursion), the values of these variables are not shared.
这意味着在您的方法中定义的变量始终是线程安全的。对方法的每次调用都有自己的这些变量版本。如果该方法由另一个线程或同一个线程调用,或者即使该方法调用自身(递归),也不会共享这些变量的值。
Thread scheduling is not guaranteed to be round-robin. A task may totally hog the CPU at the expense of threads of the same priority. You can use Thread.yield() to have a conscience. You can use (in java) Thread.setPriority(Thread.NORM_PRIORITY-1) to lower a thread's priority
线程调度不保证是循环的。任务可能会以相同优先级的线程为代价完全占用CPU。你可以使用Thread.yield()来保持良知。您可以使用(在java中)Thread.setPriority(Thread.NORM_PRIORITY-1)来降低线程的优先级
Plus beware of:
另外要注意:
- the large runtime cost (already mentionned by others) on applications that iterate over these "thread-safe" structures.
- Thread.sleep(5000) is supposed to sleep for 5 seconds. However, if somebody changes the system time, you may sleep for a very long time or no time at all. The OS records the wake up time in absolute form, not relative.
迭代这些“线程安全”结构的应用程序的大运行时成本(已被其他人提及)。
Thread.sleep(5000)应该睡5秒钟。但是,如果有人改变系统时间,你可能会睡很长时间或根本没有时间。操作系统以绝对形式记录唤醒时间,而不是相对时间。
#13
3
I would like to add some more info on top of other good answers.
我想在其他好的答案之上添加更多信息。
Thread safety implies multiple threads can write/read data in same object without memory inconsistency errors. In highly multi threaded program, a thread safe program does not cause side effects to shared data.
线程安全意味着多个线程可以在同一对象中写入/读取数据而不会出现内存不一致错误在高度多线程程序中,线程安全程序不会对共享数据造成副作用。
Have a look at this SE question for more details:
有关详细信息,请查看此SE问题:
线程安全是什么意思?
Thread safe program guarantees memory consistency.
线程安全程序保证内存一致性。
From oracle documentation page on advanced concurrent API :
从高级并发API的oracle文档页面:
Memory Consistency Properties:
内存一致性属性:
Chapter 17 of The Java™ Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.
Java™语言规范的第17章定义了内存操作的先发生关系,例如共享变量的读写。只有在读取操作之前发生写入操作时,一个线程的写入结果才能保证对另一个线程的读取可见。
The synchronized
and volatile
constructs, as well as the Thread.start()
and Thread.join()
methods, can form happens-before relationships.
synchronized和volatile构造以及Thread.start()和Thread.join()方法可以形成before-before关系。
The methods of all classes in java.util.concurrent
and its subpackages extend these guarantees to higher-level synchronization. In particular:
java.util.concurrent及其子包中所有类的方法将这些保证扩展到更高级别的同步。特别是:
- Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.
- Actions in a thread prior to the submission of a
Runnable
to anExecutor
happen-before its execution begins. Similarly for Callables submitted to anExecutorService
. - Actions taken by the asynchronous computation represented by a
Future
happen-before actions subsequent to the retrieval of the result viaFuture.get()
in another thread. - Actions prior to "releasing" synchronizer methods such as
Lock.unlock, Semaphore.release, and CountDownLatch.countDown
happen-before actions subsequent to a successful "acquiring" method such asLock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
on the same synchronizer object in another thread. - For each pair of threads that successfully exchange objects via an
Exchanger
, actions prior to theexchange()
in each thread happen-before those subsequent to the corresponding exchange() in another thread. - Actions prior to calling
CyclicBarrier.await
andPhaser.awaitAdvance
(as well as its variants) happen-before actions performed by the barrier action, and actions performed by the barrier action happen-before actions subsequent to a successful return from the corresponding await in other threads.
在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前。
在向执行程序提交Runnable之前,线程中的操作发生在执行开始之前。类似地,提交给ExecutorService的Callables也是如此。
由Future表示的异步计算所采取的操作发生在通过另一个线程中的Future.get()检索结果之后的操作之前。
“释放”同步器方法(如Lock.unlock,Semaphore.release和CountDownLatch.countDown)之前的操作发生在成功“获取”方法(如Lock.lock,Semaphore.acquire,Condition.await和CountDownLatch)之后的操作之前。 .await在另一个线程中的同一个同步器对象上。
对于通过Exchanger成功交换对象的每对线程,每个线程中的exchange()之前的操作发生在另一个线程中相应的exchange()之后的操作之前。
调用CyclicBarrier.await和Phaser.awaitAdvance(以及它的变体)之前的操作发生在屏障操作执行的操作之前,屏障操作执行的操作发生在从其他相应的await成功返回之后的操作之前发生线程。
#14
1
Yes and yes. It implies that data is not modified by more than one thread simultaneously. However, your program might work as expected, and appear thread-safe, even if it is fundamentally not.
是的,是的。这意味着数据不会被多个线程同时修改。但是,您的程序可能会按预期工作,并且看起来是线程安全的,即使它根本不是。
Note that the unpredictablility of results is a consequence of 'race-conditions' that probably result in data being modified in an order other than the expected one.
请注意,结果的不可预测性是“竞争条件”的结果,这可能导致数据按照预期的顺序进行修改。
#15
0
In simplest words :P If it is safe to execute multiple threads on a block of code it is thread safe*
用最简单的话说:P如果在代码块上执行多个线程是安全的,那么它是线程安全的*
*conditions apply
Conditions are mentioned by other answeres like 1. The result should be same if you execute one thread or multiple threads over it etc.
条件被其他答案提到,例如1.如果你在它上面执行一个线程或多个线程,结果应该是相同的。
#1
216
From Wikipedia:
Thread safety is a computer programming concept applicable in the context of multi-threaded programs. A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads. In particular, it must satisfy the need for multiple threads to access the same shared data, and the need for a shared piece of data to be accessed by only one thread at any given time.
线程安全是适用于多线程程序的计算机编程概念。如果一段代码在多个线程同时执行期间正常运行,则它是线程安全的。特别是,它必须满足多线程访问相同共享数据的需要,并且在任何给定时间只需要一个线程访问共享数据块。
There are a few ways to achieve thread safety:
有几种方法可以实现线程安全:
Re-entrancy:
Writing code in such a way that it can be partially executed by one task, reentered by another task, and then resumed from the original task. This requires the saving of state information in variables local to each task, usually on its stack, instead of in static or global variables.
以这样的方式编写代码,使其可以由一个任务部分执行,由另一个任务重新进入,然后从原始任务恢复。这需要将状态信息保存在每个任务的本地变量中,通常是在其堆栈上,而不是静态或全局变量中。
Mutual exclusion:
Access to shared data is serialized using mechanisms that ensure only one thread reads or writes the shared data at any time. Great care is required if a piece of code accesses multiple shared pieces of data—problems include race conditions, deadlocks, livelocks, starvation, and various other ills enumerated in many operating systems textbooks.
使用确保只有一个线程可以随时读取或写入共享数据的机制来序列化对共享数据的访问。如果一段代码访问多个共享数据,则需要非常小心 - 问题包括竞争条件,死锁,活锁,饥饿以及许多操作系统教科书中列举的各种其他问题。
Thread-local storage:
Variables are localized so that each thread has its own private copy. These variables retain their values across subroutine and other code boundaries, and are thread-safe since they are local to each thread, even though the code which accesses them might be reentrant.
变量已本地化,因此每个线程都有自己的私有副本。这些变量在子例程和其他代码边界中保留其值,并且是线程安全的,因为它们对于每个线程是本地的,即使访问它们的代码可能是可重入的。
Atomic operations:
Shared data are accessed by using atomic operations which cannot be interrupted by other threads. This usually requires using special machine language instructions, which might be available in a runtime library. Since the operations are atomic, the shared data are always kept in a valid state, no matter what other threads access it. Atomic operations form the basis of many thread locking mechanisms.
通过使用不能被其他线程中断的原子操作来访问共享数据。这通常需要使用特殊的机器语言指令,这些指令可能在运行时库中可用。由于操作是原子操作,因此无论其他线程访问它,共享数据始终保持有效状态。原子操作构成了许多线程锁定机制的基础。
read more:
http://en.wikipedia.org/wiki/Thread_safety
-
in German: http://de.wikipedia.org/wiki/Threadsicherheit
用德语:http://de.wikipedia.org/wiki/Threadsicherheit
-
in French: http://fr.wikipedia.org/wiki/Threadsafe (very short)
法语:http://fr.wikipedia.org/wiki/Threadsafe(很短)
#2
67
Thread-safe code is code that will work even if many Threads are executing it simultaneously.
线程安全代码是即使许多线程同时执行它也能工作的代码。
#3
42
A more informative question is what makes code not thread safe- and the answer is that there are four conditions that must be true... Imagine the following code (and it's machine language translation)
一个更具信息性的问题是什么使代码不是线程安全的 - 答案是有四个条件必须是真的...想象下面的代码(和它的机器语言翻译)
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 it's 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.
第一个条件是存在可从多个线程访问的内存位置。通常,这些位置是全局/静态变量,或者是可从全局/静态变量访问的堆内存。每个线程为函数/方法范围的局部变量获取它自己的堆栈帧,因此这些本地函数/方法变量otoh(它们在堆栈上)只能从拥有该堆栈的一个线程访问。
第二个条件是有一个属性(通常称为不变量),它与这些共享内存位置相关联,必须为true或有效,才能使程序正常运行。在上面的示例中,属性是“totalRequests必须准确表示任何线程执行增量语句的任何部分的总次数”。通常,在更新发生更新之前,此不变属性需要保持为true(在这种情况下,totalRequests必须保持准确的计数)才能使更新正确。
第三个条件是在实际更新的某些部分期间不保留不变属性。 (在处理的某些部分,它暂时无效或错误)。在这种特定情况下,从获取totalRequests的时间到存储更新值的时间,totalRequests不满足不变量。
竞争发生必须发生的第四个也是最后一个条件(并且代码因此不是“线程安全的”)是另一个线程必须能够在不变量被破坏时访问共享内存,从而导致不一致或行为不正确。
#4
32
I like the definition from Brian Goetz's Java Concurrency in Practice for its comprehensiveness
我喜欢Brian Goetz的Java Concurrency in Practice中的定义,因为它的全面性
"A class is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code."
“如果一个类在从多个线程访问时行为正确,则无论运行时环境对这些线程的执行进行调度或交错,并且调用代码没有额外的同步或其他协调,它都是线程安全的。 “
#5
23
As others have pointed out, thread safety means that a piece of code will work without errors if it's used by more than one thread at once.
正如其他人所指出的那样,线程安全意味着如果一个代码一次被多个线程使用,那么一段代码就可以正常工作。
It's worth being aware that this sometimes comes at a cost, of computer time and more complex coding, so it isn't always desirable. If a class can be safely used on only one thread, it may be better to do so.
值得注意的是,这有时会带来成本,计算机时间和更复杂的编码,因此并不总是令人满意。如果一个类只能在一个线程上安全使用,那么这样做可能会更好。
For example, Java has two classes that are almost equivalent, StringBuffer
and StringBuilder
. The difference is that StringBuffer
is thread-safe, so a single instance of a StringBuffer
may be used by multiple threads at once. StringBuilder
is not thread-safe, and is designed as a higher-performance replacement for those cases (the vast majority) when the String is built by only one thread.
例如,Java有两个几乎相同的类,StringBuffer和StringBuilder。区别在于StringBuffer是线程安全的,因此一次可以由多个线程使用StringBuffer的单个实例。 StringBuilder不是线程安全的,并且当String仅由一个线程构建时,它被设计为更高性能的替代品(大多数情况下)。
#6
22
Thread-safe-code works as specified, even when entered simultaneously by different threads. This often means, that internal data-structures or operations that should run uninterrupted are protected against different modifications at the same time.
线程安全代码按指定的方式工作,即使由不同的线程同时输入也是如此。这通常意味着,应该不间断运行的内部数据结构或操作可以同时受到不同修改的保护。
#7
18
An easier way to understand it, is what make code not thread-safe. There's two main issue that will make a threaded application to have unwanted behavior.
理解它的一种更简单的方法是使代码不是线程安全的。有两个主要问题会导致线程应用程序出现不需要的行为。
-
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.由共享变量的相互依赖引起的死锁如果你有两个共享变量A和B.在一个函数中,你先锁定A然后再锁定B.在另一个函数中,你开始锁定B,一段时间后,你锁定A.是一个潜在的死锁,当第二个函数等待A解锁时,第一个函数将等待B解锁。此问题可能不会在您的开发环境中发生,并且只会不时发生。为避免这种情况,所有锁必须始终处于相同的顺序。
#8
9
Yes and no.
是的,不是。
Thread safety is a little bit more than just making sure your shared data is accessed by only one thread at a time. You have to ensure sequential access to shared data, while at the same time avoiding race conditions, deadlocks, livelocks, and resource starvation.
线程安全不仅仅是确保您的共享数据一次只能被一个线程访问。您必须确保对共享数据的顺序访问,同时避免竞争条件,死锁,活锁和资源不足。
Unpredictable results when multiple threads are running is not a required condition of thread-safe code, but it is often a by-product. For example, you could have a producer-consumer scheme set up with a shared queue, one producer thread, and few consumer threads, and the data flow might be perfectly predictable. If you start to introduce more consumers you'll see more random looking results.
运行多个线程时不可预测的结果不是线程安全代码的必需条件,但它通常是副产品。例如,您可以使用共享队列,一个生产者线程和少数消费者线程设置生产者 - 消费者方案,并且数据流可能是完全可预测的。如果你开始引入更多的消费者,你会看到更多随机的结果。
#9
8
In essence, many things can go wrong in a multi threaded environment (instructions reordering, partially constructed objects, same variable having different values in different threads because of caching at the CPU level etc.).
本质上,在多线程环境中可能出现许多问题(指令重新排序,部分构造的对象,由于CPU级别的缓存等,在不同线程中具有不同值的相同变量等)。
I like the definition given by Java Concurrency in Practice:
我喜欢Java Concurrency in Practice提供的定义:
A [portion of code] is thread-safe if it behaves correctly when accessed from multiple threads, regardless of the scheduling or interleaving of the execution of those threads by the runtime environment, and with no additional synchronization or other coordination on the part of the calling code.
如果代码的一部分在从多个线程访问时行为正确,则它是线程安全的,无论运行时环境是否调度或交错执行这些线程,并且没有额外的同步或其他协调部分。调用代码。
By correctly they mean that the program behaves in compliance with its specifications.
通过正确,它们意味着程序的行为符合其规范。
Contrived example
Imagine that you implement a counter. You could say that it behaves correctly if:
想象一下,你实现了一个计数器。如果出现以下情况,您可以说它的行为正确:
-
counter.next()
never returns a value that has already been returned before (we assume no overflow etc. for simplicity) - all values from 0 to the current value have been returned at some stage (no value is skipped)
counter.next()永远不会返回之前已经返回的值(为简单起见,我们假设没有溢出等)
在某个阶段已返回从0到当前值的所有值(不跳过任何值)
A thread safe counter would behave according to those rules regardless of how many threads access it concurrently (which would typically not be the case of a naive implementation).
线程安全计数器将根据这些规则运行,无论有多少线程同时访问它(通常不是天真实现的情况)。
Note: cross-post on Programmers
注意:在程序员上交叉发布
#10
7
Simply - code will run fine if many threads are executing this code at the same time.
简单 - 如果许多线程同时执行此代码,代码将运行正常。
#11
4
Don't confuse thread safety with determinism. Thread-safe code can also be non-deterministic. Given the difficulty of debugging problems with threaded code, this is probably the normal case. :-)
不要将线程安全与确定性混淆。线程安全代码也可以是非确定性的。鉴于使用线程代码调试问题很困难,这可能是正常情况。 :-)
Thread safety simply ensures that when a thread is modifying or reading shared data, no other thread can access it in a way that changes the data. If your code depends on a certain order for execution for correctness, then you need other synchronization mechanisms beyond those required for thread safety to ensure this.
线程安全只是确保当线程正在修改或读取共享数据时,没有其他线程可以以更改数据的方式访问它。如果您的代码依赖于某个执行顺序以确保正确性,那么您需要除线程安全性所需的其他同步机制以确保这一点。
#12
3
To complete other answers:
要完成其他答案:
Synchronization is only a worry when the code in your method does one of two things:
当方法中的代码执行以下两项操作之一时,同步只是一种担忧:
- works with some outside resource that isn't thread safe.
- Reads or changes a persistent object or class field
适用于一些非线程安全的外部资源。
读取或更改持久对象或类字段
This means that variables defined WITHIN your method are always threadsafe. Every call to a method has its own version of these variables. If the method is called by another thread, or by the same thread, or even if the method calls itself (recursion), the values of these variables are not shared.
这意味着在您的方法中定义的变量始终是线程安全的。对方法的每次调用都有自己的这些变量版本。如果该方法由另一个线程或同一个线程调用,或者即使该方法调用自身(递归),也不会共享这些变量的值。
Thread scheduling is not guaranteed to be round-robin. A task may totally hog the CPU at the expense of threads of the same priority. You can use Thread.yield() to have a conscience. You can use (in java) Thread.setPriority(Thread.NORM_PRIORITY-1) to lower a thread's priority
线程调度不保证是循环的。任务可能会以相同优先级的线程为代价完全占用CPU。你可以使用Thread.yield()来保持良知。您可以使用(在java中)Thread.setPriority(Thread.NORM_PRIORITY-1)来降低线程的优先级
Plus beware of:
另外要注意:
- the large runtime cost (already mentionned by others) on applications that iterate over these "thread-safe" structures.
- Thread.sleep(5000) is supposed to sleep for 5 seconds. However, if somebody changes the system time, you may sleep for a very long time or no time at all. The OS records the wake up time in absolute form, not relative.
迭代这些“线程安全”结构的应用程序的大运行时成本(已被其他人提及)。
Thread.sleep(5000)应该睡5秒钟。但是,如果有人改变系统时间,你可能会睡很长时间或根本没有时间。操作系统以绝对形式记录唤醒时间,而不是相对时间。
#13
3
I would like to add some more info on top of other good answers.
我想在其他好的答案之上添加更多信息。
Thread safety implies multiple threads can write/read data in same object without memory inconsistency errors. In highly multi threaded program, a thread safe program does not cause side effects to shared data.
线程安全意味着多个线程可以在同一对象中写入/读取数据而不会出现内存不一致错误在高度多线程程序中,线程安全程序不会对共享数据造成副作用。
Have a look at this SE question for more details:
有关详细信息,请查看此SE问题:
线程安全是什么意思?
Thread safe program guarantees memory consistency.
线程安全程序保证内存一致性。
From oracle documentation page on advanced concurrent API :
从高级并发API的oracle文档页面:
Memory Consistency Properties:
内存一致性属性:
Chapter 17 of The Java™ Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.
Java™语言规范的第17章定义了内存操作的先发生关系,例如共享变量的读写。只有在读取操作之前发生写入操作时,一个线程的写入结果才能保证对另一个线程的读取可见。
The synchronized
and volatile
constructs, as well as the Thread.start()
and Thread.join()
methods, can form happens-before relationships.
synchronized和volatile构造以及Thread.start()和Thread.join()方法可以形成before-before关系。
The methods of all classes in java.util.concurrent
and its subpackages extend these guarantees to higher-level synchronization. In particular:
java.util.concurrent及其子包中所有类的方法将这些保证扩展到更高级别的同步。特别是:
- Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.
- Actions in a thread prior to the submission of a
Runnable
to anExecutor
happen-before its execution begins. Similarly for Callables submitted to anExecutorService
. - Actions taken by the asynchronous computation represented by a
Future
happen-before actions subsequent to the retrieval of the result viaFuture.get()
in another thread. - Actions prior to "releasing" synchronizer methods such as
Lock.unlock, Semaphore.release, and CountDownLatch.countDown
happen-before actions subsequent to a successful "acquiring" method such asLock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
on the same synchronizer object in another thread. - For each pair of threads that successfully exchange objects via an
Exchanger
, actions prior to theexchange()
in each thread happen-before those subsequent to the corresponding exchange() in another thread. - Actions prior to calling
CyclicBarrier.await
andPhaser.awaitAdvance
(as well as its variants) happen-before actions performed by the barrier action, and actions performed by the barrier action happen-before actions subsequent to a successful return from the corresponding await in other threads.
在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前。
在向执行程序提交Runnable之前,线程中的操作发生在执行开始之前。类似地,提交给ExecutorService的Callables也是如此。
由Future表示的异步计算所采取的操作发生在通过另一个线程中的Future.get()检索结果之后的操作之前。
“释放”同步器方法(如Lock.unlock,Semaphore.release和CountDownLatch.countDown)之前的操作发生在成功“获取”方法(如Lock.lock,Semaphore.acquire,Condition.await和CountDownLatch)之后的操作之前。 .await在另一个线程中的同一个同步器对象上。
对于通过Exchanger成功交换对象的每对线程,每个线程中的exchange()之前的操作发生在另一个线程中相应的exchange()之后的操作之前。
调用CyclicBarrier.await和Phaser.awaitAdvance(以及它的变体)之前的操作发生在屏障操作执行的操作之前,屏障操作执行的操作发生在从其他相应的await成功返回之后的操作之前发生线程。
#14
1
Yes and yes. It implies that data is not modified by more than one thread simultaneously. However, your program might work as expected, and appear thread-safe, even if it is fundamentally not.
是的,是的。这意味着数据不会被多个线程同时修改。但是,您的程序可能会按预期工作,并且看起来是线程安全的,即使它根本不是。
Note that the unpredictablility of results is a consequence of 'race-conditions' that probably result in data being modified in an order other than the expected one.
请注意,结果的不可预测性是“竞争条件”的结果,这可能导致数据按照预期的顺序进行修改。
#15
0
In simplest words :P If it is safe to execute multiple threads on a block of code it is thread safe*
用最简单的话说:P如果在代码块上执行多个线程是安全的,那么它是线程安全的*
*conditions apply
Conditions are mentioned by other answeres like 1. The result should be same if you execute one thread or multiple threads over it etc.
条件被其他答案提到,例如1.如果你在它上面执行一个线程或多个线程,结果应该是相同的。