# Esercizi thread interference

1\) (1 punto) Quale metodo viene invocato su un oggetto di tipo thread per mandare un thread in esecuzione?

2\) (1 punto) Qual'è il metodo dell'interfaccia `Runnable` che deve essere implementato per creare una classe di tipo `Runnable`?

3\) (1 punto) Scrivete il codice di un thread che scrive 5 volte "CIAO", compresa la parte per lanciare il thread in esecuzione.

4\) (3 punti) Il metodo `interrupt()` invocato su una variabile di tipo thread che cosa genera se il thread sta eseguendo la `sleep()`?

5\) (3 punti) All'interno di un thread t1 viene fatto partire un thread t2.

Quale metodo va invocato su t2 per far si che t1 interrompa la sua esecuzione finché t2 non sia terminato?

6\) (2 punti) L'accesso a una variabile condivisa da parte di due o più thread si chiama:

\- deadlock

\- sequenza critica

\- `InterruptedException`

7\) (1 punti) Quali sono le due tecniche di base utilizzate da Java per evitare che l'accesso a una variabile condivisa da parte di più thread dia risultati inconsistenti?

8\) (2 punti) Abbiamo una variabile di tipo *Contatore* con un metodo per decrementare il valore, *decrement()* e un metodo che restituisce il valore *getValue()*;

Qui sotto l'esempio di come si deve comportare un oggetto di tipo *Contatore*.

```java
Contatore contatore = new Contatore(5);
contatore.decrement();
int valore = contatore.getValue(); // ritorna valore=4
contatore.decrement();
valore = contatore.getValue(); // ritorna valore=3Java
```

Implementare la classe *Contatore* scrivendo il codice, in modo che l'accesso da parte di più thread non crei una sequenza critica nel caso due thread eseguano contemporaneamente l'operazione di *decrement()*.

**RISPOSTA:**&#x20;

Una semplice classe *Contatore* che implementa i due metodi come esposti sopra potrebbe essere:

```java
public class Contatore {
    
    private int value;
    
    public Contatore(int value) {
        this.value = value;
    }

    public void decrement() {
        value--;
    }

    public int getValue() {
        return value;
    }

}

```

Ora per rendere sicura la sezione critica del metodo *decrement()* o lo rendo *synchronized* o in modo equivalente creo un *blocco synchronized* sull'oggetto (con *this*). Sotto le due versioni equivalenti.

```java
// versione metodi synchronized

public class Contatore {

    private int value;
    
    public Contatore(int value) {
        this.value = value;
    }

    public synchronized void decrement() {
        value--;
    }

    public synchronized int getValue() {
        return value;
    }

}

```

<pre class="language-java"><code class="lang-java"><strong>// versione blocchi synchronized
</strong><strong>
</strong><strong>public class Contatore {
</strong>
    private int value;
    
    public Contatore(int value) {
        this.value = value;
    }

    public void decrement() {
        synchronized(this) {
            value--;
        }
    }

    public int getValue() {
        synchronized(this) {
            return value;
        }
    }

}

</code></pre>

Anche il metodo *getValue()* è stato sincronizzato per garantire la relazione di *happends-before* su variabili condivisa. Vedi: [Memory Consistency Properties](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html).

9\) (2 punti) Implementare una classe *Contatore* come quella precedente ma che permetta a due thread t1 e t2 a uno di accedere al metodo *increment()* e all’altro a quello *getValue()* contemporaneamente, mentre non possono accedere contemporaneamente a *increment()* o a *getValue()*.

**RISPOSTA:**

La soluzione è avere due oggetti distinti su cui ottenere il lock utilizzando i *blocchi synchonized*:

<pre class="language-java"><code class="lang-java"><strong>
</strong><strong>public class Contatore {
</strong>    
    private int value;
    
    private Object obj1 = new Object();
    private Object obj2 = new Object();
    
    public Contatore(int value) {
        this.value = value;
    }

    public void decrement() {
        synchronized(obj1) {
            value--;
        }
    }

    public int getValue() {
        synchronized(obj2) {
            return value;
        }
    }

}

</code></pre>

In questo modo un thread può acquisire il lock su *obj1* per eseguire il metodo `decrement()` e contemporaneamente un altro thread può acquisire il lock su *obj2* per eseguire il metodo `getValue()`.

Vedi:

{% embed url="<https://github.com/checksound/SimulatoreAccessoContatoreRandom>" %}

10\) (4 punti) E’ data la classe *Decrementer* di tipo `Thread` che agisce su una variabile di tipo *Contatore* e decrementa il contatore finché non arriva a 0 e quindi termina l'esecuzione del thread (codice qui sotto).

```java
public class Decrementer extends Thread {

		private Contatore contatore;

		public Decrementer(Contatore contatore) {
			this.contatore = contatore;
		}

		public void run() {
				
				while(true) {
						
						boolean isZero = (0 == contatore.getValue());  // step 1
						if(isZero)  // CONDIZIONE D'USCITA step 2
							return;
						else
							contatore.decrement();  // step 3
				
				}  // fine while
			
		}  // fine run

}
```

Immaginate due thread t1 e t2 di tipo *Decrementer* vengono mandati in esecuzione contemporaneamente passandogli una stessa variabile di tipo *Contatore*.

**Domanda 1**: Con quale flusso di esecuzione potrebbe succedere che i due thread, t1 e t2, non terminino non arrivando mai alla condizione d'uscita?

**Domanda 2**: Come si potrebbe cambiare il codice per far si che invece i due thread terminino?

### SOLUZIONE

{% embed url="<https://github.com/checksound/EsempioContatoreDecrementer>" %}
Esempio contatore decrement
{% endembed %}
