块级同步

11

synchronized接收的参数有什么意义?

synchronized(parameter)
{

}

为了实现块级同步,在某处看到了如下代码:

class test
{
    public static final int lock = 1;

    ...

    synchronized(lock) {
       ...
    }
}

我不理解这段代码的目的。

有没有人能给我一个更好的例子并/或者解释一下?

5个回答

14

这是需要加锁的引用。基本上,两个线程不会同时执行使用相同引用同步的代码块。就像Cletus所说的那样,一个同步的 方法 大多等同于在方法内部使用 synchronized (this)

我非常希望你看到的示例代码不要像那样 - 你正在尝试对原始变量进行同步。同步仅适用于监视器(通过引用)- 即使这是合法代码,x 也会被装箱,这将导致一些非常奇怪的行为,因为某些整数将永远装箱为相同的引用,而其他整数每次装箱时都会创建一个新对象。幸运的是,Java编译器意识到这是一个非常糟糕的想法,并且会为您发布的代码提供编译时错误。

更合理的代码是:

class Test
{
    private static final Object lock = new Object();

    ...

    synchronized(lock){
       ...
    }
}

我已将锁定设为私有,并将其类型更改为Object 。无论是否应该静态取决于情况 - 基本上,如果您想从多个线程访问/更改静态数据,则通常使用静态变量;当您想从多个线程访问/更改每个实例数据时,通常使用实例变量作为锁。


没错 - 我会修改。 (在上下文中,静态可能是可以的。) - Jon Skeet
1
公共和静态锁也不是一个好主意 :-) 顺便说一下,在Java中,整数1始终装箱为完全相同的对象。 - Peter Štibraný
彼得:确实 - 因此是“一些整数”。但是,如果行为因从127更改为128而更改,那将非常奇怪。(我错误地认为它是有效但不幸的代码 - 但事实证明我编译了错误的文件。糟糕。) - Jon Skeet
是的。我也在想那是否合法,但事实证明这是不可能的代码。 - Peter Štibraný
@JonSkeet:“由于某些整数始终被装箱为相同的引用,而其他整数每次装箱时都会创建一个新对象”,你能解释一下吗?我看不出可能的副作用。 - Geek
1
@Geek:考虑一下x是一个int。范围在-128到127之间的值保证每次都会装箱到相同的引用 - 而超出该范围,则取决于具体实现。因此,在某些实现中,两个在值x上同步的代码块将互相阻塞 - 而在其他实现中则不会。 - Jon Skeet

10

这个:

public synchronized void blah() {
  // do stuff
}

语义上等同于:

public void blah() {
  synchronized (this) {
    // do stuff
  }
}

有些人不喜欢使用“this”来进行同步,部分原因是它是公共的(即实例对外部代码可见)。这就是为什么有些人会使用私有锁的原因:

public class Foo
  private final static String LOCK = new String("LOCK");

  public void blah() {
    synchronized (LOCK) {
      // do stuff
    }
  }
}

LOCK不会在类外部可见,而且您可以创建多个锁以处理更细粒度的锁定情况。


我认为在这个例子中,你需要移除 "static",因为使用 "static" 会使 LOCK 对于 Foo 类的所有实例都起作用。 - ultraon
Java中字符串是不可变的,这会对锁产生任何影响吗? - Daniel Pop

3

由于代码无法编译,我无法解释。原始类型不能被锁定。

另一个不好的例子是将其更改为

public static final Integer lock =1;

这是一个不好的想法,因为小的自动装箱原始类型会被缓存,并且很可能会产生奇怪的副作用(如果这样做超过一次)。

你能解释一下副作用吗? - Geek

3
synchronized语句的目的是确保在多线程应用程序中,只有一个线程可以在任何给定时间访问关键数据结构。
例如,如果您允许两个线程同时更改同一数据结构,那么数据结构将被破坏,因此通常会使用锁来保护它。
在现实世界中,考虑一个公共厕所需要一把钥匙,挂在一个中央位置。在你使用厕所之前,你需要这把钥匙,不仅是为了进入,而且还是确保没有其他人会在同一时间尝试进入同一间厕所的保证。他们必须等待钥匙变得可用。
这就是锁定机制的工作原理。
在这种情况下,synchronized关键字的参数是钥匙。你锁住钥匙,做你需要做的事情,然后解锁钥匙让其他人访问它。如果其他线程尝试在另一个线程正在锁定该钥匙时锁定该钥匙,则该线程将不得不等待。

1
传递给 synchronized 的对象实例是“锁定单元”。试图在相同实例上获得锁定的其他线程都将等待它们的轮到。
人们使用这种方法而不是方法级别的 synchronized 关键字(它锁定执行类对象的实例),是因为他们可能希望根据多线程算法的设计,等待锁定的粒度更细或者不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接