2016-08-14 71 views
5

我已经写了一些多线程代码在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

你同步的问题锁定什么。我建议你不要使用子类Thread,因为这会导致令人惊讶的结果。 –

回答

10

添加​​方法就像在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; 
    } 
} 
+0

对Resource.i的读访问权限也必须同步,以确保更新的正确可见性。 –

+0

@ J.B你的意思是,阅读'Resource.i' _after_'join()'这两个线程?那么'join()'会创建一个[happen-before relation](https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5)(_All线程中的动作发生 - 在任何其他线程从该线程上的join()成功返回之前),以便该部分正确。 – Roman

1

让我们从Oracle文档页面来看看synchronized methods定义。

制作方法​​有两个作用:

首先,它是不可能的同一对象来交织上同步方法的两个调用。当一个线程正在执行一个对象的同步方法时,所有其他线程调用同一对象的同步方法块(挂起执行),直到第一个线程完成对象。

回来到您的查询:

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

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

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

其他解决方案(S):使用更好的并发结构,如ReentrantLock

您找到相关SE问题几个备选方案:

Avoid synchronized(this) in Java?