Keskustelut - Java - notify() vs. notifyAll() + InterruptedException wait():sta


coderodde 17:44 16.8.12 
Terve!

(Rinnakkais-)ohjelmointikysymys, joten on lähdettävä jostain liikkeelle; "tässä koodi!":

Java
public class SemaphoreMonitor {
    private volatile int permits;
   
    public SemaphoreMonitor(int permits){
        if (permits < 1) {
            throw new IllegalArgumentException("Invalid semaphore requested.");
        }
       
        this.permits = permits;
    }
   
    public synchronized void acquire() {
        while (permits == 0) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
       
        permits--;
    }
   
    public synchronized void release() {
        permits++;
        notify();
    }
   
    static class FunkyDigits {
        private int[] digits;
        private SemaphoreMonitor mutex;
        private long minDelay = Long.MAX_VALUE;
        private long maxDelay = Long.MIN_VALUE;
       
        FunkyDigits(){
            mutex = new SemaphoreMonitor(1);
            digits = new int[20];
           
            for (int i = 0; i < digits.length; i++) {
                digits[i] = i + 1;
            }
        }
       
        public String toString(){
            StringBuilder sb = new StringBuilder(1000);
           
            for(int i : digits) {
                sb.append(i);
                sb.append(' ');
            }
           
            return sb.toString();
        }
       
        void reverse(){
            long ta = System.nanoTime();
            mutex.acquire();
            long tb = System.nanoTime();
           
            if (minDelay > tb - ta) {
                minDelay = tb - ta;
            }
           
            if (maxDelay < tb - ta) {
                maxDelay = tb - ta;
            }
           
            for (int i = 0, j = digits.length - 1; i < j; i++, j--) {
                int tmp = digits[i];
                digits[i] = digits[j];
                digits[j] = tmp;
            }
           
            mutex.release();
        }
       
        long getMinDelay() {
            return minDelay;
        }
       
        long getMaxDelay() {
            return maxDelay;
        }
    }
   
    static class FunkyThread extends Thread {
        private FunkyDigits digits;
        long invocations;
       
        FunkyThread(FunkyDigits digits) {
            this.digits = digits;
        }
       
        public void run() {
            long ta = System.currentTimeMillis();
           
            while (System.currentTimeMillis() - ta < 1000L) {
                digits.reverse();
                invocations++;
            }
        }
    }
   
    public static void main(String[] args) {
        FunkyDigits digits = new FunkyDigits();
        FunkyThread ft1 = new FunkyThread(digits);
        FunkyThread ft2 = new FunkyThread(digits);
        FunkyThread ft3 = new FunkyThread(digits);
        FunkyThread ft4 = new FunkyThread(digits);

        ft1.start();
        ft2.start();
        ft3.start();
        ft4.start();

        try {
            ft1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            ft2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            ft3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            ft4.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
       
        System.out.println(digits);
        System.out.println("Invocations by ft1: " + ft1.invocations);
        System.out.println("Invocations by ft2: " + ft2.invocations);
        System.out.println("Invocations by ft3: " + ft3.invocations);
        System.out.println("Invocations by ft4: " + ft4.invocations);
        System.out.println("Maximum delay: " + digits.getMaxDelay() + " ns.");
    }
}
 

Outermost luokka (SemaphoreMonitor) yrittää mallintaa semafooria. Lisäksi meillä on käytössä luokka (FunkyDigits), joka pitää sisällään 20 int-arvoa. (FD:n konstruktori asettaa taulukon arvoihin 1, 2, ..., 20.) Koodista löytyy myöskin FD:n operaatiota reverse() toistuvasti suorittava säieluokka FunkyThread. FD:n reverse() metodi käyttää ym. SemaphoreMonitor:ia synkronointiin.

Toistaiseksi ohjelmaa näyttää toimivan: ilman synkronointia ohjelma tulostaa hassunnäköisen palindromin, ja SM:ää käytettäessä tulee laskeva/nouseva alkuperäis-inttien jono sen mukaan, onko reverse():a kutsuttu pariton/parillinen määrä.

Nyt asiaan:
(1) onko acquire()-metodissa InterruptedException hoidettu oikein vai rinnakkaisuusartifaktit ovat silti mahdollisia?
(2) release()-metodissa kokeilin sekä notify():n, että notifyAll():n; juuri tähän ohjelmaan ei päällepäin näyttäisi olevan vaikutusta kumpaa käytän. Osaisitko antaa esimerkin tilanteesta, jossa väärästä notify*():sta saattaisi tulla ongelmia?