`
chelsea
  • 浏览: 117804 次
  • 来自: ...
社区版块
存档分类
最新评论

synchronized : 规则, 推论与实践

    博客分类:
 
阅读更多

14.3.Synchronization.


Rule 1. synchronized:只影响多线程,不影响本线程 (Locks are owned per thread, so invoking a synchronized method from within another method synchronized on the same object will proceed without blocking, releasing the lock only when the outermost synchronized method returns.)

Rule 2. synchronized:只影响synchronized code,不影响非synchronized code. (Unsynchronized access does not wait for any locks but proceeds regardless of locks that may be held on the object.)

This is yet another reason to prefer accessor methods to public or protected fields: Using methods, you can synchronize access to the data, but you have no way to do so if the fields can be accessed directly outside your class.

Rule 3. synchronized: 只影响该类, 不影响子类或不受父类影响 (Synchronization requirements are a part of the implementation of a class.)


For example, a class that uses a private field as a lock object prevents an extended class from using the same synchronization mechanism. the extended class would have to define its own lock object (perhaps this) and override every method of the superclass to use this new synchronization mechanism.

Rule 4. synchronized: 类对象和实例对象互不影响(Acquiring the Class object lock in a static synchronized method has no effect on any objects of that class.) 

Rule 5. synchronized: 外围类实例和内嵌类实例互不影响(Another common use of the synchronized statement is for an inner object to synchronize on its enclosing object)

Like any other object, an inner object is independently synchronized. acquiring the lock of an inner object has no effect on its enclosing object's lock, nor does acquiring the lock of an enclosing object affect any enclosed inner objects. An inner class that needs to synchronize with its enclosing object must do so explicitly and a synchronized statement is a perfect toolthe alternative is to declare a synchronized method in the enclosing class just for the inner class to use.

Utility Method 1.

You can ask whether the current thread holds the lock on a given object by passing that object to the Thread class's static holdsLock method, which returns true if the current thread does hold the lock on that object. This is typically used to assert that a lock is held when needed. For example, a private method that expects to be invoked only from synchronized public methods might assert that fact:

assert Thread.holdsLock(this);

Conclusion 1. Don't Use synchronized(obj.getClass())

If you need a synchronized statement to use the same lock used by static synchronized methods, you can use the class literal for your class (see example below).It would also be wrong to use the Object method getClass to retrieve the Class object for the current instance: In an extended class, such as AttributedBody, that would return the Class object for AttributedBody not Body, and so again, different locks would be used and interference would not be prevented.

Conclusion 2. Client-side synchronization

An object can have its lock acquired, which prevents any of its synchronized methods from being invoked except by the lock holder performing the series of invocations. Similarly, you can acquire the locks of each of the objects involved and then invoke the series of methods on those objects but watch out for deadlock (see Section 14.7 on page 362). As long as the object's methods are already synchronized on the current object's lock, then other clients of the object need not use client-side synchronization.



14.4. wait, notifyAll, and notify

The wait and notification methods are defined in class Object and are inherited by all classes. They apply to particular objects, just as locks do.

There is a standard pattern that is important to use with wait and notification. The thread waiting for a condition should always do something like this:

synchronized void doWhenCondition() {
 while (!condition)
 wait();
… Do what must be done when the condition is true …
}

A number of things are going on here:

  • Everything is executed within synchronized code. If it were not, the state of the object would not be stable. For example, if the method were not declared synchronized, then after the while statement, there would be no guarantee that the condition remained TRue: Another thread might have changed the situation that the condition tests.

  • One of the important aspects of the definition of wait is that when it pauses the thread, it atomically releases the lock on the object. Saying that the thread suspension and lock release are atomic means that they happen together, indivisibly. Otherwise, there would be a race hazard: A notification could happen after the lock is released but before the thread is suspended. The notification would have no effect on the thread, effectively getting lost. When a thread is restarted after being notified, the lock is atomically reacquired.

  • The condition test should always be in a loop. Never assume that being awakened means that the condition has been satisfiedit may have changed again since being satisfied. In other words, don't change the while to an if.

Using notifyAll wakes up all waiting threads, whereas notify picks only one thread to wake up.

Multiple threads may be waiting on the same object, possibly for different conditions. If they are waiting for different conditions, you should always use notifyAll to wake up all waiting threads instead of using notify. Otherwise, you may wake up a thread that is waiting for a different condition from the one you satisfied. That thread will discover that its condition has not been satisfied and go back to waiting, while some thread waiting on the condition you did satisfy will never get awakened. Using notify is an optimization that can be applied only when:

  • All threads are waiting for the same condition

  • At most one thread can benefit from the condition being met

  • This is contractually true for all possible subclasses

Otherwise you must use notifyAll. If a subclass violates either of the first two conditions, code in the superclass that uses notify may well be broken. To that end it is important that waiting and notification strategies, which include identifying the reference used (this or some other field), are documented for use by extended classes.

In a multithreaded system you very rarely want to busy-wait. You should always suspend until told that what you are waiting for may have happened. This is the essence of thread communication with the wait and notifyAll/notify mechanism.


14.5. Details of Waiting and Notification

public final void wait(long timeout) tHRows InterruptedException

The current thread waits until one of four things happens: notify is invoked on this object and this thread is selected to be runnable; notifyAll is invoked on this object; the specified timeout expires; or the thread has its interrupt method invoked. timeout is in milliseconds. If timeout is zero, the wait will not time out but will wait indefinitely for notification. During the wait the lock of the object is released and is automatically reacquired before wait completesregardless of how or why wait completes. An InterruptedException is thrown if the wait completes because the thread is interrupted.

You can invoke these methods only from within synchronized code, using the lock for the object on which they are invoked. The invocation can be directly made from the synchronized code, or can be made indirectly from a method invoked in such code. You will get an IllegalMonitorStateException if you attempt to invoke these methods on an object when you don't hold its lock.

细节 1. Only notifications that occur after the wait commences will affect a waiting thread.

If no threads are waiting when either notifyAll or notify is invoked, the notification is not remembered. If a thread subsequently decides to wait, an earlier notification will have no effect on it.

细节 2. wait(long timeout)未必会导致 wait 在有限时间内返回

The use of a time-out is a defensive programming measure that allows you to recover when some condition should have been met but for some reason (probably a failure in another thread) has not. Because the lock of the object must be reacquired, the use of a time-out cannot guarantee that wait will return in a finite amount of time.

细节 3.wait始 终在循环中的另一个原因

It is also possible that some virtual machine implementations will allow so-called "spurious wakeups" to occurwhen a thread returns from wait without being the recipient of a notification, interruption, or time-out. This is another reason that wait should always be performed in a loop that tests the condition being waited on.



14.7. Deadlocks

One common technique is to use resource ordering. With resource ordering you assign an order on all objects whose locks must be acquired and make sure that you always acquire locks in that order. This makes it impossible for two threads to hold one lock each and be trying to acquire the lock held by the otherthey must both request the locks in the same order, and so once one thread has the first lock, the second thread will block trying to acquire that lock, and then the first thread can safely acquire the second lock.

14.10. The Memory Model: Synchronization and volatile

Rule 1. 除long和double外, 变量的读写都是原子操作; 然而这对于 get / modify / set 操作序列 (像 a++, b--) 毫无帮助, 它们总是需要被同步

The language guarantees that reading or writing any variables, other than those of type long or double, is atomicthe variable will only ever hold a value that was written by some thread, never a partial value intermixing two different writes. This means, for example, that an atomic variable that is only written by one thread and read by many threads need not have access to it synchronized to prevent corruption because there is no possibility of interference. This does not help with getmodifyset sequences (such as ++), which always require synchronization

Rule 2. 原子存取并不意味着一个线程读出来的变量永远是最新的; 事实上, 如果没有同步, 一个线程可能永远都看不见另外一个线程对变量的更新

The rules that determine how memory accesses are ordered and when they are guaranteed to be visible are known as the memory model of the Java programming language. If all reads and writes to a variable occur only when a specific monitor is held, then each read of the variable is guaranteed by the memory model to return the value that was most recently written to it.

Rule 3. 作为第二种同步机制, 使用 volatile 声明的变量能够保证一个线程读出来的变量永远是最新的

There is a second synchronization mechanism that doesn't provide the exclusive access of monitors, but that again ensures that each read of a variable returns the most recently written valuethe use of volatile variables. Fields (but not array elements) can be declared with the volatile modifier. A write to a volatile variable synchronizes with all subsequent reads of that variable. If currentValue was declared as volatile then the example code we showed would be correctly synchronized and the latest value would always be displayed. The use of volatile variables is seldom a replacement for the use of synchronized methods or statements on its own, because they don't provide atomicity across different actions. Rather, volatile variables are most often used for simple flags to indicate something has occurred, or for writing lock-free algorithms that incorporate use of the atomic variables mentioned in Section 25.9.

Rule 4. volatile 另外一个副作用就是让long或double类型的变量读写也变成原子操作


几个最佳实践: A few other synchronization actions help make multithreading work nicely:

  • Starting a thread synchronizes with the first action performed by that thread when it executes. This ensures that a newly started thread sees any data that was initialized by the creating threadincluding the thread's own fields.

  • The final action of a thread synchronizes with any action that detects that the thread has terminatedsuch as calling isAlive or invoking join on that thread. This ensures, for example, that if you join a thread you can see all data written by that thread before it terminatedsuch as the results of its computation.

  • Interrupting a thread synchronizes with any other action that determines that the thread has been interrupted, such as the thread throwing InterruptedException or another thread invoking isInterrupted on the thread.

  • The write of the default value (zero, null, or false) to any field synchronizes with the first action in any thread. This ensures that even in incorrectly synchronized programs a thread will never see arbitrary values in fieldseither a specific value written by some thread will be seen or the default value of the field will be seen.

分享到:
评论

相关推荐

    Java中的synchronized:同步方法与线程安全.md

    Java中的synchronized:同步方法与线程安全

    深入理解Java中的synchronized关键字:同步机制与应用

    Java提供了多种机制来处理并发问题,其中synchronized关键字是最基本也是最常用的同步手段之一。本文将深入探讨synchronized关键字的工作原理、使用方式以及在实际编程中的应用。 synchronized关键字是Java中实现...

    Synchronized:将Objective-C的@synchronized指令公开给Swift

    与Objective-C指令类似,Synchronized获取一个互斥锁,运行一些代码,并在代码完成或引发异常时释放该锁。链接框架可通过获得同步。 要安装它,只需将以下行添加到您的Podfile中: pod "Synchronized", "~> 4.0"您...

    掌握Java线程同步:深入理解与实践

    Java提供了多种同步机制,包括synchronized关键字、显式锁、等待/通知机制和原子变量。开发者应根据具体的应用场景和性能要求,选择合适的同步策略。通过合理使用这些同步工具,可以有效地管理多线程环境中的资源...

    java里面synchronized用法.doc

    2. 每个对象只有一个锁与之相关联。 3. 实现同步可能会产生系统开销,甚至可能造成死锁,因此应尽量避免无谓的同步控制。 synchronized 关键字是 Java 中解决多线程并发问题的重要工具之一。正确地使用 ...

    java中synchronized用法

    每个对象只有一个锁与之相关联。实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 当 synchronized 关键字加在方法上时,它锁定的是调用这个同步方法的对象。例如,在以下...

    volatile和synchronized的区别

    ### volatile与synchronized的区别 #### 一、锁的特性:互斥与可见性 在并发编程中,锁作为实现线程安全的一种手段,其核心作用在于提供两种特性:互斥和可见性。 - **互斥**:互斥是指在任何时刻,只允许一个...

    Java synchronized使用案例

    四、`synchronized`与其他并发工具类的对比 1. **ReentrantLock**:具有`synchronized`的可重入性,并且提供公平锁、非公平锁选择,支持尝试获取锁、中断锁等待等高级功能。 2. **Semaphore**:信号量,可以控制同时...

    Android synchronized 测试案例

    本测试案例深入探讨了`synchronized`的使用方法,包括同步单个对象、同步多个对象以及成功与失败的场景对比。 一、`synchronized`关键字的基本概念 `synchronized`关键字可以修饰方法或用作代码块,其主要作用是...

    volatile与synchronized的区别

    volatile与synchronized的区别,锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)

    [JAVA][synchronized的使用]

    通常,我们可以通过编写测试类`TestSynchronized`来实践`synchronized`的使用。例如,创建两个线程,分别访问一个共享的`synchronized`方法,观察其执行顺序和结果,以此验证同步的效果。 总结,`synchronized`是...

    synchronized并发讲解源码.zip

    除了锁住对象或类,`synchronized`还可以与`wait()`、`notify()`和`notifyAll()`方法结合使用,实现复杂的线程通信和同步。这些方法都是在`Object`类中定义的,只有在持有对象锁的情况下才能调用,否则会抛出`...

    Synchronized与ThreadLocal

    ### Synchronized与ThreadLocal #### 一、Synchronized机制详解 **Synchronized** 是 Java 中一个非常重要的关键字,主要用于实现线程同步。它通过在对象上加锁来确保多个线程能够安全地访问共享资源。 - **作用...

    java锁机制Synchronizedjava锁机制Synchronized

    "Java 锁机制 Synchronized" Java 锁机制 Synchronized 是 Java 语言中的一种同步机制,用于解决多线程并发访问共享资源时可能出现的一些问题。 Java 锁机制 Synchronized 的概念 在 Java 中,每个对象都可以被...

    JAVA synchronized详解

    #### 二、使用场景与语法 ##### 1. 同步方法 可以通过在方法声明前加上`synchronized`关键字来创建同步方法,例如: ```java public synchronized void method() { // 方法体 } ``` 对于静态方法,可以使用`...

    synchronized用法大全实例

    在Java多线程编程中,`synchronized`关键字是一个至关重要的工具,用于实现线程间的同步,以确保共享资源的安全访问。本实例大全将全面解析`synchronized`的使用方式,包括同步方法、同步语句块、类锁和对象锁。 ##...

    ReentrantLock与synchronized区别

    java语言 并发编程 ReentrantLock与synchronized区别 详解

    synchronized的几种示例

    在Java编程语言中,`synchronized`关键字是一个重要的并发控制机制,用于确保多线程环境下的数据一致性。本文将深入探讨`synchronized`的几种使用示例,包括方法加锁、代码块加锁(针对`this`和对象)以及静态方法...

    Synchronized关键字的用法

    ### Synchronized关键字在Java中的应用 #### 概述 `synchronized`是Java语言中的一个关键字,主要用于实现线程同步,防止多个线程同时访问共享资源而导致的数据不一致问题。通过`synchronized`关键字,开发者可以...

    synchronized关键字的实质及用法

    在Java编程语言中,`synchronized`关键字是用于实现线程同步的重要工具,它的本质在于确保多线程环境下的数据一致性与安全性。通过`synchronized`,我们可以控制对共享资源的访问,避免并发问题,如数据竞争和死锁。...

Global site tag (gtag.js) - Google Analytics