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


当前回答

一个信息量更大的问题是,是什么使代码不线程安全——答案是,有四个条件必须成立……想象一下下面的代码(它是机器语言翻译)

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.

其他回答

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

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

看看这个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.

让我们举个例子来回答这个问题:

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。

所以这段代码不是线程安全的。

从本质上讲,所有多线程问题都是由这类问题的某些变体引起的。

解决方案是确保加法和比较操作不能分开(例如,用某种同步代码包围这两个语句),或者设计一个不需要两个操作的解决方案。这样的代码是线程安全的。

线程安全代码是指即使有多个线程同时执行也能正常工作的代码。

http://mindprod.com/jgloss/threadsafe.html

线程安全代码按照指定的方式工作,即使由不同的线程同时输入。这通常意味着,应该不间断地运行的内部数据结构或操作受到保护,不会同时进行不同的修改。

完成其他回答:

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

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

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

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

另外还要注意:

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