Java 线程:对象具有同步和非同步方法

问题描述

假设在这个演示中使用了 2 个线程。假设 increment() 代码块首先执行并获取当前对象上的监视器。其他线程将无法执行方法 decrement() 吗? .

谁能帮我理解一下?

如果我运行该应用程序,其他线程能够执行非同步方法,即使被休眠 10000 ms 的线程锁定在对象上。

package com.learn.threads;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadDemo {

    int sharedVariable;

    public ThreadDemo(int sharedVariable) {
        this.sharedVariable = sharedVariable;
    }


    public synchronized void increment() throws InterruptedException {
        Thread.sleep(10000);
        this.sharedVariable++;

    }

    public void decrement() throws InterruptedException {
        this.sharedVariable--;

    }


    public static void main(String[] args) throws InterruptedException {

        ThreadDemo task = new ThreadDemo(0);

        ExecutorService incrementExecutorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 6; i++) {
            incrementExecutorService.submit(() -> {
                try {
                    task.increment();
                } catch (InterruptedException e) {
                    e.printstacktrace();
                }
            });
            Thread.sleep(5000);
            incrementExecutorService.submit(() -> {
                try {
                    task.decrement();
                } catch (InterruptedException e) {
                    e.printstacktrace();
                }
            });

        }
        Thread.sleep(35000);
        incrementExecutorService.shutdown();
        System.out.println(task.sharedVariable);

    }
}

解决方法

不,不会。

    方法上的
  1. synchronized 只是将方法的整个主体包装在 synchronized (X) {} 中的语法糖,其中 X 是实例方法的 thisYourClass.class对于静态的。这是一个严重的设计错误,除非你记录你的类的锁定行为,如果你这样做 - 任何时候你锁定其他代码可以引用的东西(thisYourClass.class 通常是公开的),你需要对此进行记录,并努力支持您在未来版本中记录的内容。

  2. synchronized 与同一个引用上的其他同步块交互,并且在 thatRef.wait/notify/notifyAll()没有别的。它本身的影响为零,你总是需要 2 个不同的线程同时命中同步块,同步同一件事,否则它没有任何用处。

粘贴的代码片段已损坏:如果某些线程调用 decrement(),其他线程可能会也可能不会观察到这一点,因为没有建立 CBCA 关系。任何读取 sharedVariable 的代码都需要锁定 ThreadDemo,并且 decrement 方法需要获得 synchronized 属性。

请注意,拥有可递增/可递减事物的工作已经存在:AtomicInteger,如果这是您的实际意图,您应该改用它(但我假设您仅将其作为示例编写)。

注意:Java 内存模型最好理解为邪恶的硬币。邪恶之处在于它会惹恼您:让代码在每次和所有测试中都运行良好,并且在您将其部署在生产服务器上的第一周,然后就像那个重要的客户获得演示一样,它休息。您必须编写代码以使 VM 永远不会抛硬币(或者更确切地说,抛硬币的结果不会影响您的代码),并且没有简单的方法可以知道正在抛硬币。线程很难做到正确,是的。为什么您认为现实世界中的大多数多线程代码都全部通过消息总线或事务数据库进行线程间通信?只要任何代码触及任何地方的任何字段,硬币就会翻转,硬币的结果决定线程是使用该字段的本地克隆副本,还是从共享副本中读取。因此,sharedVariable-- 可能会导致只有您的线程可以看到或所有线程都可以看到的递减,具体取决于邪恶硬币的结果。如果您的代码所做的取决于翻转,那么您就搞砸了,您的测试将无法捕捉到它。您可以通过在 sharedVariable-- 和 cod 读取的任何内容 sharedVariable 之间建立优先关系来避免抛硬币。 synchronized 是建立这种关系的几种方法之一。在网上搜索 'java memory model' 'synchronized' 以获取更多信息 - 但请注意,这是非常复杂的东西。