Come utilizzare attesa e comunicare in Java senza IllegalMonitorStateException?
-
23-08-2019 - |
Domanda
Ho 2 matrici e ho bisogno di moltiplicare loro e quindi stampare i risultati di ogni cella. Non appena una cella è pronta ho bisogno di stamparlo, ma per esempio ho bisogno di stampare il [0] [0] cella prima cellula [2] [0], anche se il risultato di [2] [0] è pronto prima . Così ho bisogno di stamparlo per ordine.
Quindi la mia idea è quella di rendere l'attesa fino a quando il filo della stampante multiplyThread
di informarla che la cella corretta è pronto per essere stampato e poi il printerThread
stamperà la cella e tornare ad aspettare e così via ..
Così ho questa discussione che fa la moltiplicazione:
public void run()
{
int countNumOfActions = 0; // How many multiplications have we done
int maxActions = randomize(); // Maximum number of actions allowed
for (int i = 0; i < size; i++)
{
result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
countNumOfActions++;
// Reached the number of allowed actions
if (countNumOfActions >= maxActions)
{
countNumOfActions = 0;
maxActions = randomize();
yield();
}
}
isFinished[rowNum][colNum] = true;
notify();
}
Discussione che stampa il risultato di ogni cella:
public void run()
{
int j = 0; // Columns counter
int i = 0; // Rows counter
System.out.println("The result matrix of the multiplication is:");
while (i < creator.getmThreads().length)
{
synchronized (this)
{
try
{
this.wait();
}
catch (InterruptedException e1)
{
}
}
if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
{
if (j < creator.getmThreads()[i].length)
{
System.out.print(creator.getResult()[i][j] + " ");
j++;
}
else
{
System.out.println();
j = 0;
i++;
System.out.print(creator.getResult()[i][j] + " ");
}
}
}
Ora mi lancia queste eccezioni:
Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at multiplyThread.run(multiplyThread.java:49)
linea 49 in multiplyThread
è la "notifica ()" .. Penso che ho bisogno di usare il sincronizzati in modo diverso, ma io non sono sicuro di come.
Se qualcuno può aiutare questo codice per lavorare io realmente lo apprezzerei.
Soluzione
Per essere in grado di chiamare notify () è necessario sincronizzare sullo stesso oggetto.
synchronized (someObject) {
someObject.wait();
}
/* different thread / object */
synchronized (someObject) {
someObject.notify();
}
Altri suggerimenti
Durante l'utilizzo del wait
e notify
o notifyAll
metodi in Java le seguenti cose devono essere ricordati:
- Usa
notifyAll
invece dinotify
se ci si aspetta che più di un thread sarà in attesa di un blocco. - I metodi
wait
enotify
deve essere chiamato in un contesto sincronizzato. Vedi il link per una spiegazione più dettagliata. - chiamare sempre il metodo
wait()
in un ciclo perché se più thread sono in attesa di un blocco e uno di loro ha ottenuto il blocco e ripristinare la condizione, poi gli altri thread devono verificare le condizioni dopo si svegliano per vedere se hanno bisogno aspettare ancora o può avviare l'elaborazione. - Utilizza lo stesso oggetto per chiamare
wait()
e il metodonotify()
; ogni oggetto ha un proprio lucchetto in modo da chiamarewait()
su un oggetto A enotify()
su oggetto B non ha alcun senso.
Avete bisogno di infilare questo a tutti? Mi chiedo quanto è grande il tuo matrici sono, e se non c'è alcun beneficio nell'avere stampa un thread, mentre l'altro fa la moltiplicazione.
Forse varrebbe la pena di misurare questa volta prima di fare il relativamente complesso lavoro threading?
Se si ha bisogno di infilarlo, vorrei creare 'n' thread per eseguire la moltiplicazione delle cellule (forse 'n' è il numero di core disponibili a voi), e quindi utilizzare il ExecutorService e Future meccanismo inviano più moltiplicazioni contemporaneamente.
In questo modo è possibile ottimizzare il lavoro in base al numero di core, e si sta utilizzando il livello più alto Java strumenti di filettatura (che dovrebbe rendere la vita più facile). Scrivi i risultati di nuovo in una matrice di ricezione, e poi semplicemente stampare una volta tutte le attività future hanno completato.
Diciamo che avete applicazione 'scatola nera' con una certa classe denominata BlackBoxClass
che ha metodo doSomething();
.
Inoltre, si dispone di osservatore o ascoltatore chiamato onResponse(String resp)
che verrà chiamato da BlackBoxClass
dopo tempo sconosciuto.
Il flusso è semplice:
private String mResponse = null;
...
BlackBoxClass bbc = new BlackBoxClass();
bbc.doSomething();
...
@override
public void onResponse(String resp){
mResponse = resp;
}
Diciamo che non sappiamo cosa sta succedendo con BlackBoxClass
e quando dovremmo ottenere risposta, ma non si desidera continuare il tuo codice fino ad ottenere risposta o in altre parole ottiene chiamata onResponse
. Qui entra 'Sincronizza helper':
public class SyncronizeObj {
public void doWait(long l){
synchronized(this){
try {
this.wait(l);
} catch(InterruptedException e) {
}
}
}
public void doNotify() {
synchronized(this) {
this.notify();
}
}
public void doWait() {
synchronized(this){
try {
this.wait();
} catch(InterruptedException e) {
}
}
}
}
Ora possiamo realizzare ciò che vogliamo:
public class Demo {
private String mResponse = null;
...
SyncronizeObj sync = new SyncronizeObj();
public void impl(){
BlackBoxClass bbc = new BlackBoxClass();
bbc.doSomething();
if(mResponse == null){
sync.doWait();
}
/** at this momoent you sure that you got response from BlackBoxClass because
onResponse method released your 'wait'. In other cases if you don't want wait too
long (for example wait data from socket) you can use doWait(time)
*/
...
}
@override
public void onResponse(String resp){
mResponse = resp;
sync.doNotify();
}
}
È possibile chiamare solo informare sugli oggetti in cui si è proprietari il loro monitor. Quindi è necessario qualcosa di simile
synchronized(threadObject)
{
threadObject.notify();
}
notify()
deve essere sincronizzata così
Ti destra semplice esempio ti mostri il modo giusto di usare wait
e notify
in Java.
Così creerò due classe denominata ThreadA e ThreadB . ThreadA chiamerà ThreadB.
public class ThreadA {
public static void main(String[] args){
ThreadB b = new ThreadB();//<----Create Instance for seconde class
b.start();//<--------------------Launch thread
synchronized(b){
try{
System.out.println("Waiting for b to complete...");
b.wait();//<-------------WAIT until the finish thread for class B finish
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("Total is: " + b.total);
}
}
}
e per la Classe ThreadB:
class ThreadB extends Thread{
int total;
@Override
public void run(){
synchronized(this){
for(int i=0; i<100 ; i++){
total += i;
}
notify();//<----------------Notify the class wich wait until my finish
//and tell that I'm finish
}
}
}
possiamo chiamare notifica di riprendere l'esecuzione di oggetti in attesa come
public synchronized void guardedJoy() {
// This guard only loops once for each special event, which may not
// be the event we're waiting for.
while(!joy) {
try {
wait();
} catch (InterruptedException e) {}
}
System.out.println("Joy and efficiency have been achieved!");
}
riprendere questo invocando notifica su un altro oggetto della stessa classe
public synchronized notifyJoy() {
joy = true;
notifyAll();
}
Uso semplice se si vuole come eseguire le discussioni in alternativa: -
public class MyThread {
public static void main(String[] args) {
final Object lock = new Object();
new Thread(() -> {
try {
synchronized (lock) {
for (int i = 0; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + "A");
lock.notify();
lock.wait();
}
}
} catch (Exception e) {}
}, "T1").start();
new Thread(() -> {
try {
synchronized (lock) {
for (int i = 0; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + ":" + "B");
lock.notify();
lock.wait();
}
}
} catch (Exception e) {}
}, "T2").start();
}
}
Risposta: -
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
Per questo particolare problema, perché non memorizzare i vostri vari risultati nelle variabili e poi quando l'ultimo del tuo thread viene elaborato è possibile stampare in qualsiasi formato desiderato. Ciò è particolarmente utile se si sta andando utilizzare la storia del lavoro in altri progetti.
Questo appare come una situazione per modello produttore-consumatore. Se stai usando Java 5 o fino, si può considerare l'utilizzo della coda di blocco (java.util.concurrent.BlockingQueue) e lasciare il lavoro di coordinamento filo alla realizzazione quadro / api sottostante. Vedere l'esempio da Java 5: http://docs.oracle.com /javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html o Java 7 (stesso esempio): http://docs.oracle.com/javase /7/docs/api/java/util/concurrent/BlockingQueue.html
Hai correttamente custodito il tuo blocco di codice quando si chiama il metodo wait()
utilizzando synchronized(this)
.
Ma non avete preso stessa precauzione quando si chiama il metodo notify()
senza l'utilizzo di blocco sorvegliato: synchronized(this)
o synchronized(someObject)
Se si fa riferimento alla pagina di documentazione Oracle su classe, che contiene wait()
, notify()
, metodi notifyAll()
, potete vedere qui sotto precauzione in tutti questi tre metodi
Questo metodo deve essere chiamato solo da un filo che è il proprietario di controllo di questo oggetto
Molte cose sono cambiate negli ultimi 7 anni e diamo un'occhiata in altre alternative per synchronized
in seguito domande SE:
Perché utilizzare un ReentrantLock se si può utilizzare sincronizzato (questo)?