2016-09-28 25 views
1

我瞭解變量上揮發性和靜態關鍵字之間的區別。揮發性關鍵字無靜態按預期工作

靜態變量可以被不同的實例改變,而易失性變量可以被不同的線程改變。

但是,下面的程序(複製從互聯網和很少修改)掛起,如果我刪除MY_INT變量的靜態關鍵字。

即使沒有靜態關鍵字,其他線程也應該看到變量MY_INT的更新。但是,如果我刪除靜態它掛起。

請幫我理解這個問題。

public class PrintOddAndEven extends Thread { 
    static volatile int i = 1; 

    Object lock; 

    PrintOddAndEven(Object lock) { 
     this.lock = lock; 
    } 

    public static void main(String ar[]) { 
     Object obj = new Object(); 
     PrintOddAndEven odd = new PrintOddAndEven(obj); 
     PrintOddAndEven even = new PrintOddAndEven(obj); 
     odd.setName("Odd"); 
     even.setName("Even"); 
     odd.start(); 
     even.start(); 
    } 

    @Override 
    public void run() { 
     while (i <= 10) { 
      if (i % 2 == 0 && Thread.currentThread().getName().equals("Even")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + i); 
        i++; 
        lock.notify(); 
       } 
      } 
      if (i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + i); 
        i++; 

        try { 
         lock.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
    } 
} 
+1

可能的重複[你有沒有在Java中使用volatile關鍵字?](http://stackoverflow.com/questions/106591/do-you-ever-use-the-volatile-keyword-in-java) – xenteros

+0

@xenteros不,這是一個問題的重複,它詢問'static'的含義。 –

回答

1

你的錯誤是由於這樣的事實,如果你從外地i刪除static關鍵字,你將有一個不同i每個實例的PrintOddAndEven所以在這裏,你有2情況下,你有2個不同的字段i這樣Even線程將永遠循環,因爲它的i永遠不會修改,並且Odd線程將永久等待,出於同樣的原因。當您將該字段聲明爲static時,線程將共享相同的字段i,這樣您就不會面臨錯誤。

你應該創建一個專用的類,將舉行您的計數器和使用作爲對象監視器的一個實例,你將在PrintOddAndEven實例之間共享,爲未來:

public class MyClass { 
    volatile int i = 1; 
} 

public class PrintOddAndEven extends Thread { 

    MyClass lock; 

    PrintOddAndEven(MyClass lock) { 
     this.lock = lock; 
    } 

    public static void main(String[] args) throws Exception { 
     MyClass obj = new MyClass(); 
     PrintOddAndEven odd = new PrintOddAndEven(obj); 
     PrintOddAndEven even = new PrintOddAndEven(obj); 
     odd.setName("Odd"); 
     even.setName("Even"); 
     odd.start(); 
     even.start(); 
    } 

    @Override 
    public void run() { 
     while (lock.i <= 10) { 
      if (lock.i % 2 == 0 && Thread.currentThread().getName().equals("Even")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
        lock.i++; 
        lock.notify(); 
       } 
      } 
      if (lock.i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) { 
       synchronized (lock) { 
        System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
        lock.i++; 

        try { 
         lock.wait(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } 
       } 
      } 
     } 
    } 
} 

如果你有只有一個計數器,您還可以考慮使用類AtomicInteger的實例作爲計數器和對象監視器。代碼將與上面的代碼相同,只不過您將使用new AtomicInteger(1)創建一個實例來將計數器初始化爲1,然後使用get()獲取當前值,並使用incrementAndGet()增加計數器。

+0

謝謝。我知道了!! –

1

您創建了2個PrintOddAndEven奇數和偶數線程對象,如果您從此語句中刪除Static關鍵字volatile int i = 1;i沒有保持類的級別,因此每個線程對象都有它自己的副本,當奇數線程執行它時,它更新了odd.i ++。但even.i保持爲1,並且條件未通過,並且它接縫中的線程掛起而沒有靜態。

public class PrintOddAndEven extends Thread { 
//static volatile int i = 1; 

Lock lock; 

PrintOddAndEven(Lock lock) { 
    this.lock = lock; 
} 


static class Lock { 
    volatile int i = 1; 
} 
public static void main(String ar[]) { 
    Lock obj = new lock(); 
    PrintOddAndEven odd = new PrintOddAndEven(obj); 
    PrintOddAndEven even = new PrintOddAndEven(obj); 
    odd.setName("Odd"); 
    even.setName("Even"); 
    odd.start(); 
    even.start(); 
} 

@Override 
public void run() { 
    while (lock.i <= 10) { 
     if (lock.i % 2 == 0 && Thread.currentThread().getName().equals("Even")) { 
      synchronized (lock) { 
       System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
       lock.i++; 
       lock.notify(); 
      } 
     } 
     if (lock.i % 2 == 1 && Thread.currentThread().getName().equals("Odd")) { 
      synchronized (lock) { 
       System.out.println(Thread.currentThread().getName() + " - " + lock.i); 
       lock.i++; 
       try { 
        lock.wait(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
}} 

這樣你就可以分享兩個線程之間。