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


当前回答

是的,是的。它意味着数据不会被多个线程同时修改。然而,您的程序可能会像预期的那样工作,并且看起来是线程安全的,即使它根本不是。

请注意,结果的不可预测性是“竞态条件”的结果,它可能导致数据以与预期顺序不同的顺序被修改。

其他回答

正如其他人所指出的,线程安全意味着如果一段代码同时被多个线程使用,那么它将正常工作。

值得注意的是,这有时是有代价的,计算机时间和更复杂的编码,所以它并不总是可取的。如果一个类只能安全地在一个线程上使用,那么这样做可能会更好。

例如,Java有两个几乎相同的类:StringBuffer和StringBuilder。不同之处在于StringBuffer是线程安全的,因此StringBuffer的单个实例可以同时被多个线程使用。StringBuilder不是线程安全的,它被设计为仅由一个线程构建String的情况下(绝大多数情况下)的高性能替代品。

完成其他回答:

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

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

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

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

另外还要注意:

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

我想在其他好的答案之上添加一些更多的信息。

线程安全意味着多个线程可以在同一个对象中读写数据,而不会出现内存不一致错误。在高度多线程的程序中,线程安全的程序不会对共享数据造成副作用。

看看这个SE问题,了解更多细节:

线程安全是什么意思?

线程安全程序保证内存一致性。

从oracle文档页高级并发API:

内存一致性属性:

Java™语言规范的第17章定义了内存操作(如共享变量的读写)的happens-before关系。只有当写操作发生时(在读操作之前),一个线程的写操作的结果才保证对另一个线程的读操作可见。

synchronized和volatile结构,以及Thread.start()和Thread.join()方法,可以形成happens-before关系。

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 an Executor happen-before its execution begins. Similarly for Callables submitted to an ExecutorService. Actions taken by the asynchronous computation represented by a Future happen-before actions subsequent to the retrieval of the result via Future.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 as Lock.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 the exchange() in each thread happen-before those subsequent to the corresponding exchange() in another thread. Actions prior to calling CyclicBarrier.await and Phaser.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.

从本质上讲,在多线程环境中,许多事情都可能出错(指令重新排序,部分构造的对象,由于CPU级别的缓存,相同的变量在不同的线程中具有不同的值等)。

我喜欢Java并发实践中给出的定义:

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

正确的意思是程序的行为符合它的规范。

的例子

假设您实现了一个计数器。你可以说它的行为是正确的,如果:

Counter.next()从不返回之前已经返回过的值(为了简单起见,我们假设没有溢出等) 从0到当前值的所有值都已在某个阶段返回(没有跳过任何值)

线程安全计数器将根据这些规则进行操作,而不管并发有多少线程访问它(通常不是简单实现的情况)。

注意:交叉贴在程序员上

是的,是的。它意味着数据不会被多个线程同时修改。然而,您的程序可能会像预期的那样工作,并且看起来是线程安全的,即使它根本不是。

请注意,结果的不可预测性是“竞态条件”的结果,它可能导致数据以与预期顺序不同的顺序被修改。