Example for the "java monitor pitfalls" chapter
authorJaroslav Tulach <jtulach@netbeans.org>
Tue, 10 Feb 2009 18:36:21 +0100
changeset 31508dd52950883
parent 314 03a451fc2256
child 316 41a4abecb600
Example for the "java monitor pitfalls" chapter
samples/deadlock/src/org/apidesign/javamonitorflaws/Cache.java
samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java
samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/samples/deadlock/src/org/apidesign/javamonitorflaws/Cache.java	Tue Feb 10 18:36:21 2009 +0100
     1.3 @@ -0,0 +1,40 @@
     1.4 +package org.apidesign.javamonitorflaws;
     1.5 +
     1.6 +import java.util.HashMap;
     1.7 +import java.util.Map;
     1.8 +
     1.9 +/**
    1.10 + *
    1.11 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.12 + */
    1.13 +public abstract class Cache<From,To> {
    1.14 +    private Map<From,To> cache;
    1.15 +
    1.16 +    public final To get(From f) {
    1.17 +        for (;;) {
    1.18 +            synchronized (this) {
    1.19 +                if (cache == null) {
    1.20 +                    cache = new HashMap<From, To>();
    1.21 +                }
    1.22 +                To t = cache.get(f);
    1.23 +                if (t != null) {
    1.24 +                    return t;
    1.25 +                }
    1.26 +            }
    1.27 +
    1.28 +            To newT = createItem(f);
    1.29 +
    1.30 +            synchronized (this) {
    1.31 +                To t = cache.get(f);
    1.32 +                if (t == null) {
    1.33 +                    cache.put(f, newT);
    1.34 +                    return newT;
    1.35 +                } else {
    1.36 +                    return t;
    1.37 +                }
    1.38 +            }
    1.39 +        }
    1.40 +    }
    1.41 +
    1.42 +    protected abstract To createItem(From f);
    1.43 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/CacheTest.java	Tue Feb 10 18:36:21 2009 +0100
     2.3 @@ -0,0 +1,131 @@
     2.4 +package org.apidesign.javamonitorflaws;
     2.5 +
     2.6 +import java.beans.PropertyChangeEvent;
     2.7 +import java.beans.PropertyChangeListener;
     2.8 +import org.netbeans.junit.NbTestCase;
     2.9 +
    2.10 +public class CacheTest extends NbTestCase {
    2.11 +    private MultiplyCache cache = new MultiplyCache();
    2.12 +
    2.13 +    public CacheTest(String n) {
    2.14 +        super(n);
    2.15 +    }
    2.16 +
    2.17 +    @Override
    2.18 +    protected int timeOut() {
    2.19 +        return 2000;
    2.20 +    }
    2.21 +
    2.22 +    /**
    2.23 +     * To simulate deadlock between the cache's setMultiply method and
    2.24 +     * ToDeadlock's own lock. The root cause is that setMultiply is calling
    2.25 +     * foreign code while holding the caches's <code>this</code> lock:
    2.26 +     
    2.27 +Thread Test Watch Dog: testDeadlockWithSetter
    2.28 +  org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:19
    2.29 +  org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40
    2.30 +  org.apidesign.javamonitorflaws.CacheTest.testDeadlockWithSetter:50
    2.31 +  sun.reflect.NativeMethodAccessorImpl.invoke0:-2
    2.32 +  sun.reflect.NativeMethodAccessorImpl.invoke:39
    2.33 +  sun.reflect.DelegatingMethodAccessorImpl.invoke:25
    2.34 +  java.lang.reflect.Method.invoke:597
    2.35 +  junit.framework.TestCase.runTest:168
    2.36 +  org.netbeans.junit.NbTestCase.access$200:84
    2.37 +  org.netbeans.junit.NbTestCase$2.doSomething:328
    2.38 +  org.netbeans.junit.NbTestCase$1Guard.run:265
    2.39 +  java.lang.Thread.run:619
    2.40 +Thread to deadlock
    2.41 +  org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.assertMultiplyByTen:40
    2.42 +  org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.propertyChange:36
    2.43 +  java.beans.PropertyChangeSupport.firePropertyChange:339
    2.44 +  java.beans.PropertyChangeSupport.firePropertyChange:276
    2.45 +  java.beans.PropertyChangeSupport.firePropertyChange:297
    2.46 +  org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21
    2.47 +  org.apidesign.javamonitorflaws.CacheTest$1ToDeadlock.run:28
    2.48 +  java.lang.Thread.run:619
    2.49 +     
    2.50 +     */
    2.51 +    public void testDeadlockWithSetter() throws Exception {
    2.52 +        if (Boolean.getBoolean("no.failures")) return;
    2.53 +        
    2.54 +        class ToDeadlock implements Runnable, PropertyChangeListener {
    2.55 +            int value;
    2.56 +
    2.57 +            public void run() {
    2.58 +                cache.setMultiply(10);
    2.59 +            }
    2.60 +            public void propertyChange(PropertyChangeEvent evt) {
    2.61 +                try {
    2.62 +                    Thread.sleep(500);
    2.63 +                } catch (InterruptedException ex) {
    2.64 +                    // ok
    2.65 +                }
    2.66 +                changeMultiplyToSeven();
    2.67 +            }
    2.68 +
    2.69 +            public synchronized void changeMultiplyToSeven() {
    2.70 +                cache.setMultiply(7);
    2.71 +            }
    2.72 +        }
    2.73 +        ToDeadlock toDeadlock = new ToDeadlock();
    2.74 +        cache.addPropertyChangeListener(toDeadlock);
    2.75 +        Thread t = new Thread(toDeadlock, "to deadlock");
    2.76 +        t.start();
    2.77 +
    2.78 +        Thread.sleep(100);
    2.79 +
    2.80 +        toDeadlock.changeMultiplyToSeven();
    2.81 +    }
    2.82 +
    2.83 +    /** Shows that one can deadlock with the cache's API even the API
    2.84 +     * is locally correctly synchronized.
    2.85 +Thread Test Watch Dog: testDeadlockWithTheAPICacheItself
    2.86 +  org.apidesign.javamonitorflaws.Cache.get:16
    2.87 +  org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen
    2.88 +  org.apidesign.javamonitorflaws.CacheTest.testDeadlockViaAPI:112
    2.89 +  java.lang.reflect.Method.invoke:597
    2.90 +  org.netbeans.junit.NbTestCase.access$200:84
    2.91 +  org.netbeans.junit.NbTestCase$2.doSomething:328
    2.92 +  org.netbeans.junit.NbTestCase$1Guard.run:265
    2.93 +  java.lang.Thread.run:619
    2.94 +Thread Deadlock using API
    2.95 +  org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.assertMultiplyByTen
    2.96 +  org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.propertyChange:98
    2.97 +  java.beans.PropertyChangeSupport.firePropertyChange:339
    2.98 +  java.beans.PropertyChangeSupport.firePropertyChange:276
    2.99 +  java.beans.PropertyChangeSupport.firePropertyChange:297
   2.100 +  org.apidesign.javamonitorflaws.MultiplyCache.setMultiply:21
   2.101 +  org.apidesign.javamonitorflaws.CacheTest$2ToDeadlock.run:90
   2.102 +  java.lang.Thread.run:619
   2.103 +     */
   2.104 +    public void testDeadlockViaAPI() throws Exception {
   2.105 +        if (Boolean.getBoolean("no.failures")) return;
   2.106 +        
   2.107 +        class ToDeadlock implements Runnable, PropertyChangeListener {
   2.108 +            public void run() {
   2.109 +                cache.setMultiply(10);
   2.110 +            }
   2.111 +            public void propertyChange(PropertyChangeEvent evt) {
   2.112 +                try {
   2.113 +                    Thread.sleep(500);
   2.114 +                } catch (InterruptedException ex) {
   2.115 +                    // ok
   2.116 +                }
   2.117 +                assertMultiplyByTen();
   2.118 +            }
   2.119 +
   2.120 +            public synchronized void assertMultiplyByTen() {
   2.121 +                int value =  cache.get("123");
   2.122 +                assertEquals("3*10=30", 30, value);
   2.123 +            }
   2.124 +        }
   2.125 +        ToDeadlock toDeadlock = new ToDeadlock();
   2.126 +        cache.addPropertyChangeListener(toDeadlock);
   2.127 +        Thread t = new Thread(toDeadlock, "Deadlock using API");
   2.128 +        t.start();
   2.129 +
   2.130 +        Thread.sleep(100);
   2.131 +
   2.132 +        toDeadlock.assertMultiplyByTen();
   2.133 +    }
   2.134 +}
   2.135 \ No newline at end of file
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/samples/deadlock/test/org/apidesign/javamonitorflaws/MultiplyCache.java	Tue Feb 10 18:36:21 2009 +0100
     3.3 @@ -0,0 +1,43 @@
     3.4 +package org.apidesign.javamonitorflaws;
     3.5 +
     3.6 +import org.apidesign.javamonitorflaws.Cache;
     3.7 +import java.beans.PropertyChangeListener;
     3.8 +import java.beans.PropertyChangeSupport;
     3.9 +
    3.10 +/**
    3.11 + *
    3.12 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.13 + */
    3.14 +public class MultiplyCache extends Cache<String,Integer> {
    3.15 +    private PropertyChangeSupport pcs;
    3.16 +    private int multiply;
    3.17 +    public static final String PROP_MULTIPLY = "multiply";
    3.18 +
    3.19 +    public synchronized int getMultiply() {
    3.20 +        return multiply;
    3.21 +    }
    3.22 +    public synchronized void setMultiply(int multiply) {
    3.23 +        int oldMultiply = this.multiply;
    3.24 +        this.multiply = multiply;
    3.25 +        pcs.firePropertyChange(PROP_MULTIPLY, oldMultiply, multiply);
    3.26 +    }
    3.27 +
    3.28 +    public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
    3.29 +        if (pcs == null) {
    3.30 +            pcs = new PropertyChangeSupport(this);
    3.31 +        }
    3.32 +        pcs.addPropertyChangeListener(listener);
    3.33 +    }
    3.34 +    public void removePropertyChangeListener(PropertyChangeListener listener) {
    3.35 +        if (pcs != null) {
    3.36 +            pcs.removePropertyChangeListener(listener);
    3.37 +        }
    3.38 +    }
    3.39 +
    3.40 +    @Override
    3.41 +    protected Integer createItem(String f) {
    3.42 +        return f.length() * getMultiply();
    3.43 +    }
    3.44 +
    3.45 +
    3.46 +}
    3.47 \ No newline at end of file