当前位置: 首页 > 知识库问答 >
问题:

从非同步方法调用同步方法问题

严瀚昂
2023-03-14

为什么下面的代码不能保证多个线程之间total_home数字的唯一性,即使逻辑处于同步块中。

class Country {
    private static int home = 0;
    int total_home;

    private static synchronized int counter() {
        return ++home ;
    }

    public void getTotal() {
        total_home = counter();
    }
}

public class T1 extends Thread {

private Country c;

public T1(Country c) {
    this.c = c;
}

@Override
public void run() {
    for (int i = 0; i <= 10; i++)
        c.getTotal();
}

}

public class T2 extends Thread {

private Country c;

public T2(Country c) {
    this.c = c;
}

@Override
public void run() {
    for (int i = 0; i <= 10; i++)
        c.getTotal();
}

}

public class MainClass {
public static void main(String[] args) {
    Country c = new Country();
    T1 ok = new T1(c);
    T2 ok1 = new T2(c);
    ok.start();
    ok1.start();
}

}

这是一个程序示例。试着运行5-10次,你会发现total_home的值并不是每次都是唯一的。

共有1个答案

裴永年
2023-03-14

你的代码有:

Country c = new Country();
   T1 ok = new T1(c);
   T2 ok1 = new T2(c);
   ok.start();
   ok1.start();

这应该很明显:计算在代码中执行new Country的次数。这是...一次。

因此,微不足道的是,只有一个国家对象可用。你的第一个和第二个线程都在引用它。(类似于:它们都有一个唯一的地址簿。但是这些地址簿包含相同的地址。java中的所有对象变量都是引用)。

您的同步代码不是问题,它工作正常-每次调用计数器()都返回一个唯一的值。

但是,您有两个线程,都在对您创建的同一个国家/地区对象调用getTotal()

好的。cok2。c是相同的(它们指的是同一个对象)。因此,它们的total_home值当然是相同的——系统中只有一个total_home,您每次都会覆盖它。

这段代码中有更多内容表明,您需要对java的工作原理做更多的回顾。例如,T1类T2类是完全相同的——那么为什么会有这些呢?

以下是你打算写的,我想:

public static void main(String[] args) throws Exception {
  Country c1 = new Country();
  Country c2 = new Country();
  T1 ok1 = new T1(c1);
  T1 ok2 = new T1(c2);
  ok1.start();
  ok2.start();
  System.out.println("These are guaranteed different: " + c1.total_home + " " + t2.total_home);
}

注意:目前有一条投票结果较高的评论建议,您的静态int home变量“可能”被缓存,您应该向其添加volatile。这是不正确的-已同步足以消除所有竞争条件。然而,AtomicInteger在这里是一个更好的选择——synchronized是一个相当“昂贵”的度量,AtomicInteger使用明显“更快”的原语(它不使用锁,它使用CPU的比较和设置(CAS)基础设施,速度快了几个数量级)为您提供一个有保证的唯一计数器。

 类似资料:
  • 我正试图从同步方法运行异步方法。但是我不能等待异步方法,因为我在同步方法中。我一定不理解TPL,因为这是我第一次使用它。 每个方法都需要前一个方法来完成,因为第一个方法的数据用于第二个方法。 Await运算符只能在异步方法中使用。考虑用'async'修饰符标记此方法,并将其返回类型更改为'task' 但是,如果我使用async修饰符,这将是一个异步操作。因此,如果我对的调用没有使用await运算符

  • 同步调用异步方法最安全的方法是什么?

  • 问题内容: 如果一个同步方法调用另一个同步方法,那么线程安全吗? 问题答案: 是的,将方法标记为时,您实际上是在这样做: 当线程调用从method1进入method2时,它将确保它持有对的锁定,该锁定已经存在,然后可以通过。 当线程直接进入method1或method2时,它将阻塞直到获得锁(),然后进入。 正如詹姆斯·布莱克(James Black)在评论中指出的那样,您必须了解方法主体内部的操

  • 我有点困惑。请看看下面的代码。 我确信调用此序列是可能的。 虽然我仍然有一个小小的困惑,但我们可以很容易地看到也调用方法,这是一个静态方法。方法 是调用非同步静态方法的静态同步方法。当 thread-2 获得类级锁时,为什么从 Thread-1 调用 没有被阻止? 我只是在逻辑上感到困惑,如果一个线程获得类级锁定,则该类其他非同步静态方法保持打开状态,以便从其他方法(实例方法)调用。为什么?