这是否意味着两个线程不能同时更改底层数据?或者它是否意味着当多个线程执行给定的代码段时,该代码段将以可预测的结果运行?
当前回答
让我们举个例子来回答这个问题:
class NonThreadSafe {
private int count = 0;
public boolean countTo10() {
count = count + 1;
return (count == 10);
}
countTo10方法将1加到计数器上,如果计数达到10则返回true。它应该只返回true一次。
只要只有一个线程在运行代码,这就可以工作。如果两个线程同时运行代码,就会出现各种问题。
例如,如果count从9开始,一个线程可以将1加到count(得到10),但随后第二个线程可以进入该方法,在第一个线程有机会执行与10的比较之前再次加1(得到11)。然后两个线程进行比较,发现count是11,并且都不返回true。
所以这段代码不是线程安全的。
从本质上讲,所有多线程问题都是由这类问题的某些变体引起的。
解决方案是确保加法和比较操作不能分开(例如,用某种同步代码包围这两个语句),或者设计一个不需要两个操作的解决方案。这样的代码是线程安全的。
其他回答
是的,是的。它意味着数据不会被多个线程同时修改。然而,您的程序可能会像预期的那样工作,并且看起来是线程安全的,即使它根本不是。
请注意,结果的不可预测性是“竞态条件”的结果,它可能导致数据以与预期顺序不同的顺序被修改。
正如其他人所指出的,线程安全意味着如果一段代码同时被多个线程使用,那么它将正常工作。
值得注意的是,这有时是有代价的,计算机时间和更复杂的编码,所以它并不总是可取的。如果一个类只能安全地在一个线程上使用,那么这样做可能会更好。
例如,Java有两个几乎相同的类:StringBuffer和StringBuilder。不同之处在于StringBuffer是线程安全的,因此StringBuffer的单个实例可以同时被多个线程使用。StringBuilder不是线程安全的,它被设计为仅由一个线程构建String的情况下(绝大多数情况下)的高性能替代品。
是也不是。
线程安全不仅仅是确保共享数据一次只能被一个线程访问。您必须确保对共享数据的顺序访问,同时避免竞争条件、死锁、活动锁和资源短缺。
当多个线程同时运行时,不可预知的结果并不是线程安全代码的必要条件,但这通常是一种副产品。例如,您可以使用一个共享队列、一个生产者线程和几个消费者线程来设置生产者-消费者方案,并且数据流可能完全可预测。如果你开始引入更多的消费者,你会看到更多随机的结果。
简单地说,如果许多线程同时执行这段代码,代码将运行良好。
我想在其他好的答案之上添加一些更多的信息。
线程安全意味着多个线程可以在同一个对象中读写数据,而不会出现内存不一致错误。在高度多线程的程序中,线程安全的程序不会对共享数据造成副作用。
看看这个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.