Newsgroup: it.comp.java
Titolo: while che non termina

salve,
ho un problema che mi sta assillando:
il metodo a della classe A chiama il metodo b della classe B.
poi rimane in attesa fino a quando nella classe B  si verifica un certo
evento.
A me pare una cosa banale che ho risolto con il classico 
boolean spiaR=true;
while(spiaR){
spiaR= B.b.isActive();
}
che  in debug funziona esattamente  come voglio. 
Ma in esecuzione normale non esce mai dal while.
Se perň dentro il while inserissco un System.out.print(...)  tutto
funziona normalmente.
Se al posto del System.out.print(...) metto una qualsiasi altra istruzione
(ad esempio  boollean xxa = spiaR;   ecco che nuovamente  non esce piů dal
loop.
Sto provando con Netbeans 6.1 in Ubuntu 8.04.
Non ho provato direttamente da shell.
Dov'č l'inghippo?
grazie 
Danilo
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 Dov'č l'inghippo?
L'inghippo potrebbe essere una starvation dovuta ad una attesa attiva.
Ad occhio sembra un programma multithread, č cosě?
Se si tratta di un programma di un programma multithread allora il 
problema potrebbe stare nel fatto che il thread che attende che la spia 
diventi false occupa tutta la CPU per ripetere innumerevoli volte un 
controllo che cmq č sempre negativo. Dall'altra parte c'e' il thread che 
dovrebbe attivare la spia che pero' non puo' partire perché la CPU č 
occupata a far girare il thread che controlla la spia.
Il problema non si presenta quando metti il System.out.print(...) per il 
seguente motivo. Dato che le operazioni di I/O sono lente e non 
riguardano direttamente la CPU essa mentre aspetta puo' permettersi 
avviare il thread che deve impostare la spia.
Prova a spiegarci chi č il thread che attiva la spia che magari 
riusciamo a consigliarti la soluzione.
-- 
Andrea Francia
http://andreafrancia.blogspot.com/2008/07/colinux-linux-dentro-windows.html

 }
Beh, questo non e' un gran bel modo, usi un mucchio di CPU, e re-inventi la 
ruota molto probabilmente in modo non sicuro e cmq troppo a basso livello. 
Se spieghi meglio quello che fa sia A che B possi suggeriri una soluzione a 
livello piu' alto e che non sprechi CPU.
Tommaso

 Oppure ancora non stai forzando un "happens-before".
Ho scritto un programma di esempio:
import java.util.Date;
import java.util.Timer;
final class Main {
 static final class A {
  void yourFunction() {
   boolean spiaR = true;
   while (spiaR) {
    spiaR = B.INSTANCE.isActive();
   }
  }
 }
 enum B {
  INSTANCE;
  private boolean active = true;
  boolean isActive() {
   return this.active;
  }
  void setActive(boolean active) {
   this.active = active;
   System.out.println("active flat ora e' " + active);
  }
 }
 public static void main(String[] args) {
  final A a = new A();
  // Questo timer task ci terra' informato di quello che avviene:
  final Timer timer = new Timer(true);
  timer.scheduleAtFixedRate(new java.util.TimerTask() {
   @Override
   public void run() {
    System.out.println("Still running : " + new Date());
    System.out.println("B.INSTANCE.isActive() : " +  B.INSTANCE.isActive());
    System.out.println("---");
   }
  }, 0, 1000);
  // Questo thread rendera' il flag inattivo dopo 5 secondi:
  final Thread t = new Thread(new Runnable() {
   @Override
   public void run() {
    try {
     Thread.sleep(5000);
     B.INSTANCE.setActive(false);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  });
  t.start();
  // Questa "blocchera'" per 5 secondi:
  a.yourFunction();
  System.out.println("Finito!");
 }
}
Fancendolo andare in modalita' -client (default), ottengo (tramite eclipse):
Fri Aug 08 19:34:03 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:34:04 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:34:05 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:34:06 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:34:07 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:34:08 BST 2008
B.INSTANCE.isActive() : true
---
active flat ora e' false
Finito!
Ma ora faceondolo andare in modalita' -server (piu' ottimizzata), ottengo:
Fri Aug 08 19:35:01 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:35:02 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:35:03 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:35:04 BST 2008
B.INSTANCE.isActive() : true
---
Fri Aug 08 19:35:05 BST 2008
B.INSTANCE.isActive() : true
---
active flat ora e' false
Fri Aug 08 19:35:06 BST 2008
B.INSTANCE.isActive() : false
---
Fri Aug 08 19:35:07 BST 2008
B.INSTANCE.isActive() : false
---
Fri Aug 08 19:35:08 BST 2008
B.INSTANCE.isActive() : false
---
Fri Aug 08 19:35:09 BST 2008
B.INSTANCE.isActive() : false
---
Fri Aug 08 19:35:10 BST 2008
B.INSTANCE.isActive() : false
---
Fri Aug 08 19:35:11 BST 2008
B.INSTANCE.isActive() : false
---
...
etc... non termina mai!
Ora basta aggiungere un volatile:
private volatile boolean active = true;
nella classe B e termina sia in -client che in -server mode.
In alternativa puoi' lasciare il boolean non volatile e cambiare i due 
metodi in:
synchronized boolean isActive() {
   return this.active;
}
synchronized void setActive(boolean active) {
   this.active = active;
   System.out.println("active flat ora e' " + active);
}
ovvero renderli synchronized.
Nota che ho sviluppato questo codice su di un portatile dual core (2.0Ghz 
dual core Intel T7200) e giro su windows con JDK6 standard e quindi non e' 
il problema menzionato da Andrea. Nel tuo caso specifico potresti non avere 
problemi di happens-before relation, ma potrebbe essere quello che dice 
Andrea.
Facci sapere!
Tommy

 ---
...
il che non cambiava nulla, anzi rendeva il tutto piu' chiaro.
Se ti interessa l'argomento ti consiglio di leggere:
http://java.sun.com/docs/books/tutorial/essential/concurrency/memconsist.html
o meglio ancora il libro Java Concurrency in Practice di Goetz, ma ti copio 
ed incollo un pezzo del link li sopra:
[Da: 
http://java.sun.com/docs/books/tutorial/essential/concurrency/memconsist.html
]
Memory consistency errors occur when different threads have inconsistent 
views of what should be the same data. The causes of memory consistency 
errors are complex and beyond the scope of this tutorial. Fortunately, the 
programmer does not need a detailed understanding of these causes. All that 
is needed is a strategy for avoiding them.
The key to avoiding memory consistency errors is understanding the 
happens-before relationship. This relationship is simply a guarantee that 
memory writes by one specific statement are visible to another specific 
statement.
......
There are several actions that create happens-before relationships. One of 
them is synchronization ....
.......
[Da: 
http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
]
.....
The synchronized and volatile constructs, as well as the Thread.start() and 
Thread.join() methods, can form happens-before relationships.....
.....
Tommy

 wrote
e per finire la mega discussione.... parlando di utilizzo CPU, anche quando 
il programma funziona correttamente (con relazione happens-before), il 
problema e' che per quei 5 secondi il thread che aspetta per il flag mi usa 
uno dei due core al 100% (50% delle prestazioni del mio portatile andare per 
un compito che dovrebbe essere banale!).
Quindi ora se prendi il codice originale che ho postato e ripiazzi le classi 
A e B (b e' un enum-singleton) con le due seguenti versioni di A e B 
l'utilizzo del core scende a ~0% come dovrebbe essere:
static final class A {
   void yourFunction() {
      try {
         B.INSTANCE.blockUntilFalse();
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
}
enum B {
   INSTANCE;
   private boolean active = true;
   private Object lock = new Object();
   boolean isActive() {
      synchronized (lock) {
         return this.active;
      }
   }
   void setActive(boolean active) {
      synchronized (lock) {
         this.active = active;
         if (!active) {
            lock.notifyAll();
         }
      }
      System.out.println("active flag ora e' " + active);
   }
   void blockUntilFalse() throws InterruptedException {
      synchronized (lock) {
         lock.wait();
      }
   }
}
Io fossi in te userei meccanismi piu' ad alto livello rispetto a 
wait/notify, ma fino a che non ci dici esattamente cosa stai cercando di 
risolvere, questa e' una soluzione generica a basso livello che con una 
relazione happens-before fa una blocking-wait quindi senza usare CPU fino a 
che quando il flag cambia e diventa flase non svegliamo i thread 
interessati.
In questo caso faccio qualche cosa di orrendo, sveglio solo i threads se il 
flag e' stato settato su false (lo faccio per accomodare meglio il tuo 
esempio ed il mio codice precedente). In pratica pero' vuoi fare qualche 
cosa di piu' elegante e meno error-prone (come ad esempio avere invece di 
setActive una funzione chiamate deactivate() che disattiva e chiamare in 
modo non condizionale poi notifyAll()).
Il motivo per cui uso :
private Object lock = new Object();
e poi "synchroniz"zo su questo e' perche' voglio evitare di esporre il 
"lock". Se usassi il "this" implicito, il codice esterno potrebbe 
partecipare e fare un synchronized(B.INSTANCE) e quindi non potresti piu' 
avere il controllo assoluto (all'interno della enum B) di quello che succede 
riguardo ai locks, a wait e notify etc...
Nota come a volte fare uso di synchronized e di wait() che a prima vista 
sembra rallentare il codice (synchronized non ha costo zero e wait() blocca 
l'esecuzione), spreca meno CPU e quindi rende il resto del codice piu' 
performance (supponendo che si siano altri thread che fanno altre 
operazioni).
Per wait/notify, che cmq se per quello che devi fare ci sono alternative 
migliori eviterei, guardati: 
http://java.sun.com/docs/books/tutorial/essential/concurrency/guardmeth.html
Tommy

come già ripetuto dagli altri, ci dovresti dire quanti thread ha il tuo
programma e in che modo hai deciso di utilizzarli ...deadlock e strvation
sono comuni in un programma multithread mal progettato!
-----------------------------------------------
Inviato tramite 
http://itwebnewsreader.servehttp.com

 Tommy
Scusate il ritardo con cui rispondo, ma mi sono concesso qualche giorno di
relax in montagna, da dove sono rientrato solo oggi (sig!).
 
Grazie comunque a tutti per le risposte, che confermano quello che già
sapevo. Di java ne so proprio meno di niente.
No, il programma non è multithread. Dall'alto della mia ignoranza,
ritenevo il problema così banale da non dover tirare in ballo i thread.
Sostanzialmente i metodi della clase A gesiscono dei dati:
leggono,scrivono su files (DB-SQL) e controllano i dati che arrivano dalla
classe B, i cui metodi si occupano di gestire tutto quello che riguarda il
video ( aspetto dei dati a video (colore,font, visibilità, edit,
internazionalizzazione, ecc.), controllo formale dei dati immessi
dall'utente( se numerici che il numero di interi e decimali corrisponda
alla dichiarazione del campo, se per il campo è previsto un range di
valori, che questi siano rispettati ecc).
 in alcuni casi gestisce una jtable. 
Quindi fino a quando l'utente non decide che ha finito di immettere i dati
nella videata( tramite un punto di menu, o un bottone o in altro modo), la
classe A rimane in attesa nel while, dopodiché riceve i dati impostati a
video, li controlla e si comporta di conseguenza(registrazione o
riemissione della videata con le opportune segnalazioni di errore. ed il
ciclo ricomincia.
Ho controllato l'utilizzo delle CPU. il mio PC ha 4 cpu.
Quando lancio netbeans tutte e 4 sono al 100%.
Quando lancio il run della mia applicazione, la cpu2 scende al 13%, con
delle punte al 95%, le altre restano fisse al 100%. Così fino alla
chiusura della mia applicazione , quando anche la cpu2  torna al 100%.
Bene, adesso metabolizzerò i consigli che mi avete dato e sopratutto gli
esempi di Tommy, poi proverò le soluzioni che mi ha indicato ( previa
opportuna documentazione) poi vi farò sapere.
ancora grazie.
danilo


 chiusura della mia applicazione , quando anche la cpu2  torna al 100%.
3 cores al 100% sembra tanto, ma francamente non capisco nemmeno perche' 
quando l'applicazione termini siano tuttei e 4 al 100%! (100% utilizzati?).
Tommy