在示例中 methodA 和 methodB 是实例方法(与静态方法相反)。使用synchronized
实例方法意味着线程必须在调用该方法的对象实例上获取锁(“内在锁”),然后线程才能开始执行该方法中的任何代码。
如果您有两个不同的实例方法标记为同步,并且不同的线程在同一个对象上同时调用这些方法,那么这些线程将竞争同一个锁。一旦一个线程获得锁,所有其他线程都将被该对象上的所有同步实例方法关闭。
为了使这两种方法同时运行,它们必须使用不同的锁,如下所示:
class A {
private final Object lockA = new Object();
private final Object lockB = new Object();
public void methodA() {
synchronized(lockA) {
//method A
}
}
public void methodB() {
synchronized(lockB) {
//method B
}
}
}
其中同步块语法允许指定执行线程需要获取内部锁定才能进入块的特定对象。
需要理解的重要一点是,即使我们在各个方法上加上了“同步”关键字,核心概念是幕后的内在锁。
以下是Java 教程描述这种关系的方式:
同步是围绕称为内在锁或监视器锁的内部实体构建的。(API 规范通常将此实体简称为“监视器”。)内在锁在同步的两个方面都发挥作用:强制对对象状态的独占访问和建立对可见性至关重要的先发生关系。
每个对象都有一个与之关联的内在锁。按照惯例,需要对对象字段进行排他和一致访问的线程必须在访问对象之前获取对象的内在锁,然后在完成访问时释放内在锁。在获得锁和释放锁之间,线程被称为拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在尝试获取锁时会阻塞。
锁定的目的是保护共享数据。仅当每个锁保护不同的数据成员时,您才可以使用上面示例代码中所示的单独锁。