当线程试图同时调用静态和非静态同步方法时会发生什么?

时间:2014-12-26 06:45:48

标签: java multithreading

我有一个班级

public class ThreadExample extends Thread{

    static int count = 0;
    public static synchronized  int increment(){
        return count++;
    }
    public synchronized int decrement(){
        return count--;
    }

}

这里我有一个静态方法和一个非静态方法。

第一个thread1调用了方法increment(),它是synchronized.It在类级别上获取锁定。 这里我的问题是,如果另一个thread2正在调用decrement()方法,那么thread2将获得对decrement()的锁定以及它是如何工作的?

5 个答案:

答案 0 :(得分:3)

synchronized关键字有两种可能的用途。它可以用作方法的修饰符,它可以用作语句。此外,synchronized修饰符可以与static结合使用,在这种情况下,目标对象将是封闭的,而不是封闭的实例

Scope    | modifiers           | corresponding statement
---------+---------------------+------------------------
static   | synchronized static | synchronized (X.class)
instance | synchronized        | synchronized (this)

如果方法为static synchronized,则在class的情况下,在封闭式class的{​​{1}}对象上获取锁定。 虽然它们被编译成不同的字节代码,但以下两种方法是等效的:

ThreadExample.class

如果方法是public class Foo { // static method with synchronized modifer public static synchronized void foo1() { // ... } // equivalent synchronized statement public static void foo2() { synchronized (Foo.class) { // ... } } } (没有静态),则在实例本身上获取锁。虽然它们被编译成不同的字节代码,但以下两种方法是等效的:

synchronized

因此,public class Foo { // instance method with synchronized modifier public synchronized void foo3() { // ... } // equivalent synchronized statement public void foo4() { synchronized (this) { // ... } } } increment()的{​​{1}}不同,并且可能存在竞争条件

因此,变量decrement()未得到充分保护,不会受到并发更新的影响。

synchronizedcount本身不能是原子的,因为递增或递减值需要读取 - 更新 - 写入周期。从技术上讲,它可能是原子的,因为一些CPU通过提供相应的指令为其提供原子性,这些指令将保持为自己获得的总线/地址,直到执行操作为止。但是JVM并不依赖于这些东西。

如果您需要 atomic int ,您可能需要查看++

如何在C

-- 使用VM环境方法java.util.concurrent.atomic.AtomicIntegersynchronized实现

synchronized

陷阱

当您使用MonitorEnter()修饰符时,您可以同步一些或多或少MonitorExit()的内容,即对其他对象和类也可见。 synchronized监视器功能为public java.lang.Object提供基础设施,以及原生函数synchronized / public }和等待池方法MonitorEnter()MonitorExit()wait()。如果“其他人”也使用“您的对象/您的类”进行同步,这可能会导致意外错误和死锁。

因此,它已成为实际上不使用notify()修饰符而是在notifyAll()锁定对象上使用synchronized语句的模式,如下所示:

synchronized

现在private不再被其他人同步干扰或阻止。你可能认为可能有一个合理的用例,但我认为如果你有一个用于锁定对象/类边界的用例,那么设计中可能存在一个很大的缺陷 - 事情并不是自足的。

如果您需要类锁而不是实例锁,只需将变量设为静态。

请注意,在进行序列化时,您必须处理锁定对象。最简单的方法是实际上不使用Object,但是:

public class Foo {
    private final Object lock = new Object();
    public void foo() {
        synchronized (lock) {
            // ...
        }
    }
}

如果要保存序列化存储,可以声明锁Foo并在反序列化期间重新创建锁定,但要小心public class Lock implements Serializable {} ,您需要反思transient对他们而言,但这是一个不同的故事。

答案 1 :(得分:1)

调用同步静态方法会尝试获取类对象的锁定。 (在您的情况下为ThreadExample),而调用同步非静态方法则尝试获取特定实例对象的锁。所以基本上你获得了2个不同的锁,因此你的代码不是线程安全的。由于竞争条件,数据count可能已损坏

答案 2 :(得分:1)

Oracle教程:

  

您可能想知道静态同步方法会发生什么   调用,因为静态方法与类关联,而不是   宾语。在这种情况下,线程获取内部锁   与类关联的类对象。因此可以访问类的静态   字段由锁定控制,该锁定与任何锁定不同   班级的实例。

答案 3 :(得分:0)

静态和实例级别锁是两个不同的锁,它们彼此独立。

关于有问题的场景“第一个thread1调用了方法increment(),它是synchronized.It获取类级别的锁。这里我的问题是,如果另一个thread2正在调用decrement()方法,那么thread2将获得锁定递减()及其工作方式“

即使thread1持有(类级别)锁定增量方法而线程2调用递减方法,它也应该获取(实例级别)锁定。

问题不在于在尝试更新共享变量时可能会启动锁定问题。如果两个线程同时尝试访问count变量(一个通过静态锁定,另一个通过实例级别锁定),那么那里可能是两个线程之间的 RACE 条件。

正如 Christian 所建议的那样,你可以通过使用 java.util.concurrent.atomic.AtomicInteger 代替 int

来解决这个问题。

有关详细信息,请访问URL

答案 4 :(得分:0)

静态同步方法和非静态同步方法不会相互阻塞。原因是Class实例上的静态方法锁定,而非静态方法锁定此实例 - 两个操作都不会相互干扰。

在您的代码中,一个方法是静态同步的,另一个方法是同步的。您的代码不是线程安全的,即使两个线程在同一个对象上执行两个方法,一个线程(执行非静态方法)获取对象级别锁定并继续。执行静态方法的另一个线程获取类级锁(即ThreadExample.class)并继续。请记住,静态方法总是类级别。

要使代码线程安全,您需要在示例中进行以下更改。

       public static synchronized int decrement(){
           return count--;
        }