【问题标题】:Java Thread : object has both synchronized and not synchronized methodsJava 线程:对象同时具有同步和非同步方法
【发布时间】:2020-12-28 16:40:44
【问题描述】:

假设在这个demo中使用了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);

    }
}

【问题讨论】:

  • 由于递减不同步,它不会尝试获取任何锁,而是继续执行它的操作。但它的更改可能对其他线程不可见。增量和减量运算符也不是线程安全的。所以这段代码有几个问题。
  • 谢谢。只有执行同步方法/块的线程才会检查对象上的锁。
  • 代码包含竞争条件,因为递减与递增或并发递减并不互斥。所以你可以松动更新。它还包含数据竞争,因为变量的读/写(由于非同步递减方法)不是由发生在关系之前排序的。

标签: java multithreading synchronized


【解决方案1】:

不,不会的。

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

  2. synchronized 与同一引用上的其他同步块交互,thatRef.wait/notify/notifyAll()没有别的。它本身的影响为零,您总是需要 2 个不同的线程同时访问一个同步块,同步同一事物,或者它没有任何用处。

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

请注意,拥有一个可递增/可递减的东西的工作已经存在:AtomicInteger,如果这是您的实际意图,您应该使用它来代替(但我假设您只是作为示例编写的)。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-02
    • 2012-11-15
    相关资源
    最近更新 更多