Array List - Sincronizzazione celle

di il
13 risposte

Array List - Sincronizzazione celle

Salve a tutti!
E' possibile scrivere un blocco sincronizzato rispetto ad una cella di un ArrayList di Object?
Cioè sia
ArrayList<Object> array=new ArrayList(10);
public void metodo(int i){
	synchronized(array.get(i)){...}
	}
E' possibile?
Sto svolgendo un esercizio che realizza la problematica del produttore/consumatore ma rispetto le singole celle dell'array, piuttosto che rispetto ad un intero buffer. Mi spiego meglio... Devo realizzare una classe che rappresenti un array di Object di dimensione fissa, con un metodo put(i,x) che inserisce l'oggetto x nell'i-sima cella, restando in attesa se è occupata, e un metodo take(i,x) che resta in attesa se è libera.
"Exception in thread "main" java.lang.NullPointerException" alla riga del synchronized,
questo è il risultato da cui sorge la mia domanda..
(l'array l'ho inizializzato nel costruttore, in quanto ho un esempio d'uso di un main al quale far riferimento.)

13 Risposte

  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    E' possibile scrivere un blocco sincronizzato rispetto ad una cella di un ArrayList di Object?
    	synchronized(array.get(i)){...}
    E' possibile?
    synchronized acquisisce il lock su un OGGETTO. Chiaramente il reference NON deve essere null. Quindi se alla posizione i-esima di un array o lista c'è un reference non-null, allora synchronized acquisisce il lock su quell'oggetto. Tutto qui.
  • Re: Array List - Sincronizzazione celle

    andbin ha scritto:


    violet_prog ha scritto:


    E' possibile scrivere un blocco sincronizzato rispetto ad una cella di un ArrayList di Object?
    	synchronized(array.get(i)){...}
    E' possibile?
    synchronized acquisisce il lock su un OGGETTO. Chiaramente il reference NON deve essere null. Quindi se alla posizione i-esima di un array o lista c'è un reference non-null, allora synchronized acquisisce il lock su quell'oggetto. Tutto qui.
    Quindi ho questo errore perchè tutte le celle dell'array sono inizializzate a null (come richiesto dalla traccia) e quindi alla prima chiamata di put viene lanciata l'eccezione. Allora chiaramente non è questo il modo di scrivere questo metodo.
  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    Quindi ho questo errore perchè tutte le celle dell'array sono inizializzate a null (come richiesto dalla traccia) e quindi alla prima chiamata di put viene lanciata l'eccezione.
    Evidentemente è così ...

    violet_prog ha scritto:


    Allora chiaramente non è questo il modo di scrivere questo metodo.
    Soluzione semplice: crei una classe (anche nascosta e interna alla tua) es. ObjElem che contiene un Object. Quindi la tua classe userà un List<ObjElem> e farai in modo che già alla istanziazione della tua classe, la lista sarà popolata con tutti ObjElem (chiaramente con contenuto null).
    Il lock quindi lo potrai sempre acquisire, sul ObjElem chiaramente (NON sul contenuto).
    Uno scrittore si bloccherà se il contenuto è non-null; un lettore si bloccherà se il contenuto è null.
  • Re: Array List - Sincronizzazione celle

    andbin ha scritto:


    Soluzione semplice: crei una classe (anche nascosta e interna alla tua) es. ObjElem che contiene un Object. Quindi la tua classe userà un List<ObjElem> e farai in modo che già alla istanziazione della tua classe, la lista sarà popolata con tutti ObjElem (chiaramente con contenuto null).
    Il lock quindi lo potrai sempre acquisire, sul ObjElem chiaramente (NON sul contenuto).
    Uno scrittore si bloccherà se il contenuto è non-null; un lettore si bloccherà se il contenuto è null.
    Avevo scartato questa possibilità in quanto il caso d'uso sul quale devo sviluppare la classe mi da alcune informazioni sul come dev'essere:
     BlockingArray<String> array=new BlockingArray(10);
    per cui avevo immaginato una classe del tipo
    public class BlockingArray<Object> {
    
        private List<Object> q;
        public BlockingArray(int n){
            q=new ArrayList(n);
            for(int i=0;i<n;i++)
                q.add(i,null);
        }
        public void put(int i, Object x){
            synchronized(q.get(i))
            {
                while(q.get(i)!=null){
                    try {
                        q.get(i).wait();
                    } catch (InterruptedException ex) {
                        return;
                    }}
            
            q.add(i, x);
            q.get(i).notify();
            }
        }
        public Object take(int i) {
            synchronized(q.get(i))
            {
                while(q.get(i)==null){
                    try {
                        q.get(i).wait();
                    } catch (InterruptedException ex) {
                        
                    }}
            Object x=q.remove(i);
            q.get(i).notify(); 
            return x;
            }
        }
    Che ovviamente NON VA BENE, per quanto detto. (e magari anche per altre motivazione )
  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    per cui avevo immaginato una classe del tipo
    public class BlockingArray<Object> {
    Innanzitutto questo <Object> è una type variable, NON la classe java.lang.Object. Proprio per evitare confusioni e problemi, per le type variable si usa tipicamente una singola lettera, es. <T> o <E> o altro. Mettere un nome che è uguale a quello di una classe reale, esistente è FUORVIANTE e INAPPROPRIATO.

    violet_prog ha scritto:


            synchronized(q.get(i))
    Ti ho già detto che non va bene fare il synchronized sugli elementi della lista che possono essere anche null.

    Quindi: usa APPROPRIATAMENTE i generics (finora non l'hai fatto) e ti ripeto, segui il mio suggerimento di prima (classe interna che incapsula l'oggetto), perché è una soluzione valida ed appropriata.
  • Re: Array List - Sincronizzazione celle

    andbin ha scritto:


    Quindi: usa APPROPRIATAMENTE i generics (finora non l'hai fatto) e ti ripeto, segui il mio suggerimento di prima (classe interna che incapsula l'oggetto), perché è una soluzione valida ed appropriata.
    Lo stavo facendo, ecco quello che stavo scrivendo nel frattempo
    public class BlockingArray<T> {
    
        private List<ElemObj> q;
        public BlockingArray(int n){
            q=new ArrayList(n);
            for(int i=0;i<n;i++)
                q.add(i,new ElemObj(null));
        }
        private class ElemObj{
            private T elem;
            public ElemObj(T x){
                this.elem=x;
            }
        }
        public void put(int i, T x){
            synchronized(q.get(i))
            {
                while(q.get(i).elem!=null){
                    try {
                        q.get(i).wait();
                    } catch (InterruptedException ex) {
                        return;
                    }}
            
            q.add(i, new ElemObj(x));
            q.get(i).notify();
            }
        }
        public T take(int i) {
            synchronized(q.get(i))
            {
                while(q.get(i).elem==null){
                    try {
                        q.get(i).wait();
                    } catch (InterruptedException ex) {
                        
                    }}
            ElemObj x=q.remove(i);
            q.get(i).notify(); 
            return x.elem;
            }
        }
    Ma mi lancia un eccezione :
    Exception in thread "main" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)

    Eppure il notify lo chiamo su q.get(i) di cui il metodo possiede il mutex
  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    Eppure il notify lo chiamo su q.get(i) di cui il metodo possiede il mutex
    Riguardo la ElemObj, l'hai fatta come "inner class". Non è strettamente necessario (non deve usare nulla della classe "contenitore"). La puoi mettere come "nested (static) class".
    Il punto è che se è una inner class, il <T> di BlockingArray è visibile e in scope. Se la metti come nested static class, NON lo è, devi dichiarare una nuova type variabile e idem, per evitare confusione, meglio mettere un'altra lettera es. <E>

    Poi:
    q.add(i, new ElemObj(x));

    NON ha senso. Non devi aggiungere un nuovo ElemObj ma semplicemente settare il elem interno a quello su cui tenevi il lock. Se crei un nuovo ElemObj rischi di mandare tutto a pallino (altro oggetto, lock differente!).

    ElemObj x=q.remove(i);

    Idem. NON devi rimuovere.



    Ah:
    q=new ArrayList(n);

    stai usando il raw type. E' più corretto:

    q=new ArrayList<ElemObj>(n);

    o da Java 7:

    q=new ArrayList<>(n);
  • Re: Array List - Sincronizzazione celle

    andbin ha scritto:


    violet_prog ha scritto:


    Eppure il notify lo chiamo su q.get(i) di cui il metodo possiede il mutex
    Riguardo la ElemObj, l'hai fatta come "inner class". Non è strettamente necessario (non deve usare nulla della classe "contenitore"). La puoi mettere come "nested (static) class".
    Il punto è che se è una inner class, il <T> di BlockingArray è visibile e in scope. Se la metti come nested static class, NON lo è, devi dichiarare una nuova type variabile e idem, per evitare confusione, meglio mettere un'altra lettera es. <E>
    "nested static class" non fa parte del mio programma... per cui non so neanche cosa sia purtroppo. Posso approfondire per l'apprendimento personale, ma non posso utilizzarlo per svolgere questo esercizio.
    Poi:
    q.add(i, new ElemObj(x));

    NON ha senso. Non devi aggiungere un nuovo ElemObj ma semplicemente settare il elem interno a quello su cui tenevi il lock. Se crei un nuovo ElemObj rischi di mandare tutto a pallino (altro oggetto, lock differente!).

    ElemObj x=q.remove(i);

    Idem. NON devi rimuovere.
    Per aggiungere intendi all'ArrayList? ,lo aggiungo perchè quello che mi interessa a me alla fine è l'arraylist con indice ed oggetto.
    Se invece ti riferisci al "new ElemObj(x)", se non creo l'oggetto come faccio a settare elem? non esiste elem se non ho un istanza dell'oggetto ElemObj.
  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    andbin ha scritto:


    violet_prog ha scritto:


    Eppure il notify lo chiamo su q.get(i) di cui il metodo possiede il mutex
    Riguardo la ElemObj, l'hai fatta come "inner class". Non è strettamente necessario (non deve usare nulla della classe "contenitore"). La puoi mettere come "nested (static) class".
    Il punto è che se è una inner class, il <T> di BlockingArray è visibile e in scope. Se la metti come nested static class, NON lo è, devi dichiarare una nuova type variabile e idem, per evitare confusione, meglio mettere un'altra lettera es. <E>
    "nested static class" non fa parte del mio programma... per cui non so neanche cosa sia purtroppo. Posso approfondire per l'apprendimento personale, ma non posso utilizzarlo per svolgere questo esercizio.
    Poi:
    q.add(i, new ElemObj(x));

    NON ha senso. Non devi aggiungere un nuovo ElemObj ma semplicemente settare il elem interno a quello su cui tenevi il lock. Se crei un nuovo ElemObj rischi di mandare tutto a pallino (altro oggetto, lock differente!).

    ElemObj x=q.remove(i);

    Idem. NON devi rimuovere.
    Per aggiungere intendi all'ArrayList? ,lo aggiungo perchè quello che mi interessa a me alla fine è l'arraylist con indice ed oggetto.
    Se invece ti riferisci al "new ElemObj(x)", se non creo l'oggetto come faccio a settare elem? non esiste elem se non ho un istanza dell'oggetto ElemObj.
    Devo anche rimovere.. me lo chiede la traccia.
  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    "nested static class" non fa parte del mio programma... per cui non so neanche cosa sia purtroppo. Posso approfondire per l'apprendimento personale, ma non posso utilizzarlo per svolgere questo esercizio.
    Ehm ... ok, no prob. Va bene anche inner class.

    violet_prog ha scritto:


    Per aggiungere intendi all'ArrayList? ,lo aggiungo perchè quello che mi interessa a me alla fine è l'arraylist con indice ed oggetto.
    Se invece ti riferisci al "new ElemObj(x)", se non creo l'oggetto come faccio a settare elem? non esiste elem se non ho un istanza dell'oggetto ElemObj.
    Ripeto che non devi aggiungere/rimuovere gli ElemObj. Hai già creato all'inizio un ArrayList con N ElemObj. quei tot sono e rimangono!

    Il campo 'elem', nonostante sia private, è perfettamente accessibile dalla classe contenitore (BlockingArray).
  • Re: Array List - Sincronizzazione celle

    andbin ha scritto:


    violet_prog ha scritto:


    "nested static class" non fa parte del mio programma... per cui non so neanche cosa sia purtroppo. Posso approfondire per l'apprendimento personale, ma non posso utilizzarlo per svolgere questo esercizio.
    Ehm ... ok, no prob. Va bene anche inner class.

    violet_prog ha scritto:


    Per aggiungere intendi all'ArrayList? ,lo aggiungo perchè quello che mi interessa a me alla fine è l'arraylist con indice ed oggetto.
    Se invece ti riferisci al "new ElemObj(x)", se non creo l'oggetto come faccio a settare elem? non esiste elem se non ho un istanza dell'oggetto ElemObj.
    Ripeto che non devi aggiungere/rimuovere gli ElemObj. Hai già creato all'inizio un ArrayList con N ElemObj. quei tot sono e rimangono!

    Il campo 'elem', nonostante sia private, è perfettamente accessibile dalla classe contenitore (BlockingArray).
    Compilando con l'esempio d'uso fornito dal prof., fa esattamente quello che ci si aspettava. Grazie mille! Dopo averti risposto, ho riflettuto meglio sulle tue parole e ci sono arrivata in ritardo ahhaha
    Posso però migliorare il codice?
    public class BlockingArray<T> {
    
        private List<ElemObj> q;
        public BlockingArray(int n){
            q=new ArrayList(n);
            for(int i=0;i<n;i++)
                q.add(i,new ElemObj(null));
        }
        private class ElemObj{
            private T elem;
            public ElemObj(T x){
                this.elem=x;
            }
        }
        public void put(int i, T x){
            ElemObj e=q.get(i);
            synchronized(e)
            {
                while(e.elem!=null){
                    try {
                        e.wait();
                    } catch (InterruptedException ex) {
                        return;
                    }}
            e.elem=x;
            e.notify();
            }
        }
        public T take(int i) {
            ElemObj e=q.get(i);
            synchronized(e)
            {
                while(e.elem==null){
                    try {
                        e.wait();
                    } catch (InterruptedException ex) {
                        
                    }}
            T x=e.elem;
            e.elem=null;
            e.notify();
            return x;
            }
        }
  • Re: Array List - Sincronizzazione celle

    violet_prog ha scritto:


    Posso però migliorare il codice?
    A parte stile di scrittura, denominazioni, ecc.. è migliorabile per almeno 2 cose:

    - incapsulare la logica "bloccante" in ElemObj
    - fare in modo che InterruptedException possa uscire da put/take. Se un thread A è bloccato nella put/take e un altro thread B lo interrompe con interrupt(), put/take non hanno granché da fare in risposta a questo, cioè a parte del "logging" non c'è nient'altro di sensato che possono fare. La cosa migliore è far uscire InterruptedException.

    P.S. adesso sono al lavoro ed ho poco tempo, appena riesco ti posto qui o a te in privato (come preferisci) la "mia" soluzione.
  • Re: Array List - Sincronizzazione celle

    andbin ha scritto:


    A parte stile di scrittura, denominazioni, ecc.. è migliorabile per almeno 2 cose:

    - incapsulare la logica "bloccante" in ElemObj
    - fare in modo che InterruptedException possa uscire da put/take. Se un thread A è bloccato nella put/take e un altro thread B lo interrompe con interrupt(), put/take non hanno granché da fare in risposta a questo, cioè a parte del "logging" non c'è nient'altro di sensato che possono fare. La cosa migliore è far uscire InterruptedException.

    P.S. adesso sono al lavoro ed ho poco tempo, appena riesco ti posto qui o a te in privato (come preferisci) la "mia" soluzione.
    Ti ringrazio molto. Anche in privato va bene.
Devi accedere o registrarti per scrivere nel forum
13 risposte