【问题标题】:Why is the synchronized method not accessed synchronously in this multithreaded program?为什么在这个多线程程序中没有同步访问 synchronized 方法?
【发布时间】:2016-08-14 06:47:31
【问题描述】:

我在 java 中编写了一些多线程代码和更改变量的同步方法,但它没有同步我的代码,我仍然得到随机值。有我的代码:

public class Main {
    public static void main(String[] args) throws Exception {
        Resource.i = 5;
        MyThread myThread = new MyThread();
        myThread.setName("one");
        MyThread myThread2 = new MyThread();
        myThread.start();
        myThread2.start();
        myThread.join();
        myThread2.join();
        System.out.println(Resource.i);
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        synMethod();
    }

    private synchronized void synMethod() {
        int i = Resource.i;
        if(Thread.currentThread().getName().equals("one")) {
            Thread.yield();
        }
        i++;
        Resource.i = i;
    }
}

class Resource {
    static int i;
}

有时我得到 7,有时是 6,但我已经同步了 synMethod,据我所知,当其他线程执行此方法时,任何线程都不应该使用此方法,因此操作应该是原子的,但它们不是,我不明白为什么?您能否向我解释一下,并回答 - 我该如何解决?

【问题讨论】:

  • 您锁定的内容同步很重要。我建议您不要将 Thread 子类化,因为这会导致令人惊讶的结果。

标签: java multithreading synchronized


【解决方案1】:

添加synchronized 方法就像在this 上同步。由于您有两个不同的线程实例,它们不会相互锁定,而且这种同步实际上并没有做任何事情。

为了使同步生效,您应该在一些共享资源上进行同步。在您的示例中,Resource.class 可能是一个不错的选择:

private void synMethod() { // Not defined as synchronized
    // Synchronization done here:
    synchronized (Resource.class) {
        int i = Resource.i;
        if (Thread.currentThread().getName().equals("one")) {
            Thread.yield();
        }
        i++;
        Resource.i = i;
    }
}

【讨论】:

  • Resource.i 的读取访问权限也必须同步,以确保更新的正确可见性。
  • @J.B 你的意思是,在两个线程上阅读Resource.i after join()ing?好吧,join() 创建了一个 happens-before relation线程中的所有操作都发生 - 在任何其他线程从该线程上的 join() 成功返回之前),所以这部分是正确的。
【解决方案2】:

让我们看一下 oracle 文档页面中synchronized methods 的定义。

创建synchronized 方法有两个效果:

首先,同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象。

回到您的查询:

synMethod() 是同步方法对象级别。访问相同synchronized 方法的两个线程按顺序获取对象锁。但是访问不同实例(对象)的同步方法的两个线程在没有共享锁的情况下异步运行。

myThreadmyThread2 是两个不同的对象 => 内在锁是在两个不同的对象中获取的,因此您可以异步访问这些方法。

一种解决方案:正如 Mureinik 所引用的,使用共享对象进行锁定。

其他解决方案:使用更好的并发构造,例如 ReentrantLock 等。

您可以在相关的 SE 问题中找到更多替代方案:

Avoid synchronized(this) in Java?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    相关资源
    最近更新 更多