内在锁和同步

同步是建立在一个内部的实体上,被称作内在锁或者监视锁。(同步锁的api中经常涉及这个实体的简单的称作“监视器”)内在锁在同步的两方面都扮演了一个角色:强制占有一个对象的状态和建立happens-before关系,这样基本上可见。

每个对象都有一个与之关联的内在锁。按照惯例,一个线程,需要独占和一致的一个对象的字段,在进入这个对象前得需要这个对象的内在锁,当处理完这些字段后,需要释放这些内在锁。只有有一个线程拥有一个内在锁,其他的线程就不能获取这同样的锁。当其他试图获取这把锁时将被阻塞。

当一个线程释放了内在锁,就在那个动作和同一把锁的子需求之间建立了happens-before的关系。

在同步方法中的锁

当线程调用同步方法时,它自动的为这个方法的对象获取一个内在锁并在方法返回的时候释放锁。即使返回是由于一个异常导致的,锁释放也会发生。

你可能想知道当一个static 同步方法被调用的时候会发生什么,因为一个静态方法是与一个类关联的,而不是一个对象。在这种情况下,线程为与这个类关联的类对象获取内在锁。然后进入被锁控制的类的静态字段,那与任何类的一个实例的锁是有明显区别的。

同步语句

  另一种方法创建synchronized代码通过同步语句。不像同步方法,同步语句必须指定提供内在锁的的对象:

  public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
  }

  在这个例子中,addName方法需要多lastName和nameCount字段有一个同步变化,但也需要避免其他对象方法的同步调用。(从同步代码中调用其他对象的方法会引发一些问题,这些会在活性这部分讲述)。没有同步块,不得不将有一个分开的,非同步的方法来调用nameList.add。
 
  同步块也对改进有细粒度的同步的并发有用。例如,类MsLunch有两个实例字段,从不一起使用。所有这些字段的更新都是同步的。但是没有理由阻止c1的更新从交错排列的c2的更新--并这样做通过创建非必要阻塞减少并发。代替同步方法或者与之使用与之相关的锁,我们单独的创建两个对象来提供锁。

  public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            c2++;
        }
    }
  }

使用这种极端保健的方式。你必须绝对确定受影响的字段的内在锁是安全的。

可重入同步(Reentrant Synchronization)
   回顾那个一个线程不能获取另一个线程拥有的锁。但是一个线程可以获取它已经拥有的锁。可重入同步允许一个线程多次获取同样的锁成为可能。这描述了一种情形同步代码直接或者间接调用一个包含了同步代码的方法,并且两组代码使用相同的锁。没有可重入同步,同步代码将不得话费更多的措施来避免一个线程调用自身来阻塞。



  

你可能感兴趣的:(内在锁和同步)