2015-05-27 31 views
5

Bu kodu çalıştırarak, test değişkenini 5 saniyeliğine artırmayı ve ardından bitirmeyi beklerim. Bunu çalıştırdığınızdaDöngü sona ermezken döngü süresi

import java.util.Timer; 
import java.util.TimerTask; 

public class Test { 
    private static boolean running; 

    public static void main(String[] args) { 
     long time = 5 * 1000;  // converts time to milliseconds 
     long test = Long.MIN_VALUE; 
     running = true; 

     // Uses an anonymous class to set the running variable to false 
     Timer timer = new Timer(); 
     timer.schedule(new TimerTask() { 
      @Override 
      public void run() { running = false; } 
     }, time); 

     while(running) { 
      test++; 
     } 

     timer.cancel(); 
     System.out.println(test); 
    } 
} 

Ancak bitmez programı (ı varsayalım, ben zaman makul bir miktar verdik). Ben

while(running) { 
     System.out.println(); 
     test++; 
    } 

iken döngü değiştirmek Ancak program zaman beklenen miktarda bitirir (ve çizgilerin bir sürü yazdırır). Anlamıyorum Bu davranış neden ortaya çıkıyor?

+0

Eğer 'uçucu' çalıştırıyorsanız? –

+0

Sadece kodunuzu JDJ 7'yi IntelliJ 11 ile test ettim ve her iki versiyon da birkaç saniyede bitti. Kaçmak için ne kullanıyorsun? –

+0

@AndyTurner vay, daha önce hiç volatile anahtar sözcüğe rastlamadım. Thats kod çalışır. Teşekkürler! – DenverCoder9

cevap

4

Java Memory Model uyarınca, uçucu olmayan bir alanın başka bir iş parçacığı tarafından görülebileceğinin garantisi yoktur. Sizin durumunuzda ana yönteminiz JIT derlenmiş ve JIT-derleyici makul bir şekilde akım iş parçacığının bir döngüde running değişkenini değiştirmediğini varsaymaktadır, o zaman her yinelemede okumaya zahmet etmemeli ve döngüyü sonsuza dönüştürmemeliyiz. Bu davada bile FindBugs warning var.

Bir System.out.println çağrısı eklediğinizde, JIT derleyicinin satır içi yapamayacağından, bu yöntemin running değişkenini değiştirmediğinden emin olamaz, böylece alan okuma optimizasyonunu kapatır. Bununla birlikte, sorun çözümü olarak düşünülmemelidir: Java'nın daha yeni sürümlerinin daha akıllı olması ve içeride System.out.println varsa bile döngüsünüzü optimize etmesi mümkündür.

+0

Bu cevabı kabul ettim, çünkü ek bilgileri bağlar. Teşekkürler! – DenverCoder9

2

kodunuzu debug, işe yarayacak ... senin koduna yaklaşmak sağlar. Çünkü sadece bir iş parçacığı ve önbellek görmeyeceksin. Java, iş parçacığı tabanlı önbellek mekanizmaları kullanabilir. Ana belleğe okumalı ve yazmalısın.

Çalışan değişkeninizde volatile anahtar sözcüğünü kullanırsanız, jvm bunu birden fazla iş parçacığı tarafından düzenlenebilir olarak görür ve önbelleğe alınmamalıdır.

Bu nedenle, sorununuzda çalışan değişkeninize volatile ekleniyor.

2

running değişkenini main yönteminizden farklı bir iş parçacığında güncelliyorsunuz. Java Bellek Modeli, farklı iş parçacıklarındaki değişken olmayan değişkenlere yönelik güncellemeleri görmeyi garanti etmeyeceğiniz şekildedir.

En kolay çözüm, volatile değişkenini yapmaktır: bu, iş parçacığı eriştiğinde kontrol edilecek değişkenin 'geçerli' değerini zorlar.

Diğer çözümler yerine boolean arasında AtomicBoolean kullanılarak ve karşılıklı synchronized blok (yani running kere kodu güncelleştirir kodu ile aynı monitörde eşitlenir) içerisinde running erişimi ambalaj içerir.

Bu sorunu ayrıntılı olarak açıklayan Java Concurrency In Practice uygulamasının bir kopyasını almanızı kesinlikle öneririm.