2014-01-10 85 views
1

我看不出最终的计数器值为20000.这段代码有什么问题?方法同步线程问题

public class Synchronize2 { 

    public static void main(String[] args) { 
     Threading t1 = new Threading(); 
     Threading t2 = new Threading(); 

     t1.start(); 
     t2.start(); 

     try { 
      t1.join(); 
      t2.join(); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 

     System.out.println(Threading.counter); 
    } 
} 


class Threading extends Thread { 

    static int counter; 

    public synchronized void incrementer() { 
     counter++; 
    } 

    public void run() { 
     for (int i=0; i<10000; i++) { 
      incrementer(); 
     } 
    } 
} 
+2

您正在同步两个不同的对象。 – SLaks

+0

@SLaks。增量值字段是静态的 –

+1

@MarkusMalkusch:正确。他的代码不是线程安全的。 – SLaks

回答

3

您同步的incrementer方法将锁定对象本身。但是你有两个不同的对象,每个对象都自己锁定,所以这个方法不是线程安全的;两个线程仍然可以同时访问incrementer

此外,后增加操作不是线程安全的,因为它不是原子的;有一个读操作和一个增量操作,并且一个线程可以在两个操作中间被中断。这个非线程安全的代码提供了一个竞争条件,其中线程一读取值,线程二读取值,然后线程一个增量和线程两个增量,但只有最后一个增量“赢”和一个增量丢失。这显示了当末端值小于20000

使该方法static过,所以因为它的​​,它将类,它是在这里适当的同步的类对象上的锁。

public static synchronized void incrementer() { 
+2

或者使用'AtomicInteger'并完全放弃'synchronized'。 –

+0

可能很好解释,因为它们没有锁定在同一个对象上,所以++不是线程安全的。如果他改变为静态方法,那么这将解决线程安全问题。 – Gray

0

您在两个不同的对象上同步。你的提取器是这样的一个简短形式:

public void incrementer() { 
     synchronized (this) { 
      counter++; 
     } 
    } 

但是“this”的两个实例不是相同的Object。因此,你根本不同步。试试这个方法:

private static Object sync = new Object(); 

    public void incrementer() { 
     synchronized (sync) { 
      counter++; 
     } 
    } 

你也应该使变量计数器易变。这里不是必须的,因为你只在同步块中使用它。但在实际的代码中,你可能会在这个块之外读取它,然后你会遇到问题。非易失性变量可以从本地线程缓存中读取,而不是从内存读取。

+0

如果你正在走下'synchronized'和'volatile'路线,那么我强烈建议使用'AtomicInteger' - 这很可能在稍后导致问题。 –

+0

是的,你当然是对的。我认为这是一个关于线程的问题,而不是关于递增整数。因此,我专注于线程部分的答案 – Matthias