2012-07-21 11 views
6

Mam pętlę while działającą w mojej głównej metodzie programu Java. Pętla ma działać, dopóki zmienna flagi boolowskiej nie zostanie ustawiona na true w metodzie keyPressed programu (dodałem program jako KeyListener do JFrame).Podczas gdy pętla nie kończy się po zmianie flagi w innym wątku

import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import javax.swing.JFrame; 

public class ThreadWhile implements KeyListener { 
    private boolean flag = false; 

    public static void main(String[] args) { 
     //Set up key listening on a dummy JFrame 
     JFrame frame = new JFrame("Key Detector 9000"); 
     frame.setVisible(true); 
     ThreadWhile tw = new ThreadWhile(); 
     frame.addKeyListener(tw); 

     System.out.println("Looping until flag becomes true..."); 
     while(!tw.flag) { 
      //Commenting the println out makes the loop run forever! 
      System.out.println(tw.flag); 
     } 
     System.out.println("Flag is true, so loop terminated."); 
     System.exit(0); 
    } 

    public void keyPressed(KeyEvent e) { 
     flag = true; 
     System.out.println("flag: " + flag); 
    } 

    public void keyReleased(KeyEvent e) {} 
    public void keyTyped(KeyEvent e) {} 
} 

To jest moje zrozumienie, że keyPressed metody wykonać we własnych wątków, więc wydaje się, kiedy uderzę klucza, zmienna „flaga” powinna być ustawiona na wartość true, a pętla przy uruchomionej w głównej metody powinien się skończyć.

JEDNAK, kiedy uruchamiam ten program, pętla działa wiecznie, nawet jeśli widzimy, że zmienna 'flag' jest ustawiona prawidłowo! Co dziwne, program zachowuje się poprawnie, jeśli wstawię szybkie echo System.out.println zmiennej 'flag' wewnątrz pętli while, ale oczywiście nie chcę niczego drukować w pętli.

Zgaduję, że ten problem może być wynikiem kompilatora Java, który próbuje zoptymalizować pustą pętlę while do punktu, w którym przestaje sprawdzać zmienną "flag"? Czy ktoś ma sugestie, aby to działało poprawnie, lub może bardziej sprytne podejście oparte na współbieżności, aby główny wątek wstrzymał się, dopóki wątek keyPressed nie zostanie wykonany?

Dzięki!

+0

Stosować strzeżone bloki zamiast (http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html). Zapętlenie i oczekiwanie, że coś się spełni, zepsuje wydajność aplikacji i sprawi, że przestanie reagować. –

Odpowiedz

9

Należy zadeklarować flagę volatile, w przeciwnym razie kompilator może zoptymalizować kod i pominąć odczyty flagi.

+0

Świetnie, że go rozwiązałem. Dzięki! – user1543221

4

Choć volatile rozwiązanie, które inni proponowane powinno działać, chyba że trzeba kod w while pętli wykonać w sposób ciągły, należy prawdopodobnie zamiast używać wait() i notify() (lub notifyAll()) wewnątrz zsynchronizowany części (w celu uniknięcia „zajęty Czekanie"). Coś jak:

public class ThreadWhile implements KeyListener { 
    private boolean flag = false; 
    private Object flagLock = new Object(); 

    public static void main(String[] args) { 
     //Set up key listening on a dummy JFrame 
     JFrame frame = new JFrame("Key Detector 9000"); 
     frame.setVisible(true); 
     ThreadWhile tw = new ThreadWhile(); 
     frame.addKeyListener(tw); 

     System.out.println("Waiting until flag becomes true..."); 
     synchronized (tw.flagLock) { 
      while(!tw.flag) 
       tw.flagLock.wait(); // Note: this suspends the thread until notification, so no "busy waiting" 
     } 
     System.out.println("Flag is true, so loop terminated."); 
     System.exit(0); 
    } 

    public void keyPressed(KeyEvent e) { 
     synchronized (flagLock) { 
      flag = true; 
      System.out.println("flag: " + flag); 
      flagLock.notifyAll(); 
     } 
    } 

    ... 

przeciwnym razie są wielokrotnie tracić cykli na głównym wątku sprawdzając wartość flag kółko (co, ogólnie rzecz biorąc, jeśli zdarza się na tyle, może spowolnić CPU do innych wątków i może zużywać baterię na urządzeniach mobilnych). To jest właśnie ta sytuacja, dla której zaprojektowano wait().

0

1. Trzeba użyć volatile kluczowe dla flagi, bo gdy my nazywamy zmiennością na zmiennej instancji, a następnie jej jak mówienie JVM, aby upewnić się, że wątek dostępu musi pogodzić własną kopię tego Zmienna instancji z tą zapisaną w pamięci.

2. lotne będą także pomaga w zapobieganiu buforowania wartości w wątku.

3. Lotny sprawi, że zmienna odzwierciedla jego zmienioną wartość jednego wątku do drugiego, ale nie wyklucza sytuacji wyścigu.

Tak więc użyj zsynchronizowanego słowa kluczowego w instrukcji Atomic, która ustawia wartość flagi.

Np:

public void keyPressed(KeyEvent e) { 
    synchronized(this){ 
     flag = true; 
     System.out.println("flag: " + flag); 
     } 
    } 
Powiązane problemy