为什么在这个多线程程序中不同步访问同步方法?

时间:2016-08-14 06:47:31

标签: java multithreading synchronized

我在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,因为据我所知,在执行此方法时,没有任何线程应该使用此方法,因此操作应该是原子的,但它们是不,我不明白为什么?你可以向我解释一下,并回答 - 我该如何解决?

2 个答案:

答案 0 :(得分:10)

添加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;
    }
}

答案 1 :(得分:1)

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

制作方法synchronized有两个影响:

  

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

回到您的查询:

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

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

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

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

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

Avoid synchronized(this) in Java?