`
ayagen
  • 浏览: 12664 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

synchronized

阅读更多
synchronized的原子性
synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A,没有的话,直接运行它包括两种用法:synchronized 方法和 synchronized 块。

1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
  public synchronized void accessVal(int newVal);
  synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放
(?那Reentrancy又如何理解?:
from <<Java Concurrency in Practice>>:
When a thread requests a lock that is already held by another thread, the requesting thread blocks. But because intrinsic locks are reentrant, if a thread tries to acquire a lock that it already holds, the request succeeds.
Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.
Reentrancy is implemented by associating with each lock an acquisition count and an owning thread. When the count is zero, the lock is considered unheld. When a thread acquires a previously unheld lock, the JVM records the owner and sets the acquisition count to one. If that same thread acquires the lock again, the count is incremented, and when the owning thread exits the synchronized block, the count is decremented. When the count reaches zero, the lock is released.
关键是要理解上文中加粗部分的描述自怎么会发生:比如在一个类中有两个同步方法
public  synchronized A(){ ... B(); ..};
private synchronized B(){ ... .... ..};
当前线程thread1 进入方法A时获得了当前实例instance1的锁L1(这时JVM会记录下L1的owner 为thread1, 记数count从0变为1),
当执行到调用B方法的语句时,因为B也是被同步的,因此thread1需要去请求当前实例的锁L1,这个时候(JVM检查到L1的owner就是thread1,于是count从1变为2)由于reentrancy的存在,thread1获得了锁L1,开始执行方法B.当退出方法B时,count减为1,继续执行方法A,当退出方法A时,count减为0,JVM将锁L1的owner记为空null.至此方法A释放了锁L1
因此可以理解,如果没有特征reentrancy,调用方法B时将因为L1还没有被释放而阻塞,而L1的释放又必须在A方法执行完之后,因些必然会造成死锁)

此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
  在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
  synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。

  2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
  synchronized(syncObject) {
  //允许访问控制的代码
  }
  synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
  对synchronized(this)的一些理解
  一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
  二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
  三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞
  四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分(包括同步方法和同步块)的访问都被暂时阻塞。
  五、以上规则对其它对象锁同样适用


synchronized的"内存可见性"
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics