sk.baka.webvm.analyzer.Deadlock.java Source code

Java tutorial

Introduction

Here is the source code for sk.baka.webvm.analyzer.Deadlock.java

Source

/**
 * Copyright 2009 Martin Vysny.
 *
 * This file is part of WebMon.
 *
 * WebMon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * WebMon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with WebMon.  If not, see <http://www.gnu.org/licenses/>.
 */
package sk.baka.webvm.analyzer;

import java.lang.management.ManagementFactory;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.SystemUtils;
import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

/**
 * Simulates a deadlock.
 * @author Martin Vysny
 */
public final class Deadlock {

    // we cannot use ReentrantLock as JVM 1.5 does not detect this type of deadlock.
    private final Lock lock1 = new ReentrantLock();
    private final Lock lock2 = new ReentrantLock();
    private Thread t1 = null;
    private Thread t2 = null;
    /**
     * If one of the threads fail then the cause is stored here.
     */
    public volatile Throwable t = null;

    /**
     * Simulates a deadlock. The deadlock should be already simulated when the method finishes.
     */
    @Before
    public void simulate() {
        if (t1 != null || t2 != null) {
            return;
        }
        t1 = new Thread("deadlock1") {

            @Override
            public void run() {
                try {
                    lock1.tryLock();
                    try {
                        Thread.sleep(100);
                        lock2.tryLock(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
                        lock2.unlock();
                    } catch (InterruptedException ex) {
                        // okay
                    } finally {
                        lock1.unlock();
                    }
                } catch (Throwable ex) {
                    t = ex;
                }
            }
        };
        t1.setDaemon(true);
        t2 = new Thread("deadlock2") {

            @Override
            public void run() {
                try {
                    lock2.tryLock();
                    try {
                        Thread.sleep(100);
                        lock1.tryLock(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
                        lock1.unlock();
                    } catch (InterruptedException ex) {
                        // okay
                    } finally {
                        lock2.unlock();
                    }
                } catch (Throwable ex) {
                    t = ex;
                }
            }
        };
        t2.setDaemon(true);
        t1.start();
        t2.start();
        try {
            Thread.sleep(200);
        } catch (InterruptedException ex) {
            Logger.getLogger(Deadlock.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Cancels the deadlock, disposing of the threads.
     */
    @After
    public void cancel() {
        if (t1 != null && t2 != null) {
            t1.interrupt();
            t2.interrupt();
            try {
                t1.join();
                t1 = null;
            } catch (InterruptedException ex) {
                Logger.getLogger(Deadlock.class.getName()).log(Level.SEVERE, null, ex);
            }
            try {
                t2.join();
                t2 = null;
            } catch (InterruptedException ex) {
                Logger.getLogger(Deadlock.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * Checks that the threads are running and no exception was thrown.
     */
    @Test
    public void checkThreads() {
        assertNull("An exception was thrown while forming a deadlock: " + t, t);
        assertTrue("Both threads must be alive as they are deadlocked", t1.isAlive());
        assertTrue("Both threads must be alive as they are deadlocked", t2.isAlive());
        // sanity-check for JVM to report correct values
        final long ids[] = ProblemAnalyzer.findDeadlockedThreads(ManagementFactory.getThreadMXBean());
        // this assumption fails on 1.5 as it does not support finding deadlocks in a ReentrantLock.
        // we could form a deadlock using the synchronized keyword but there is no way to interrupt
        // wait in the synchronized block thus the threads will never end - this will interfere with other tests.
        // Just skip the tests on 1.5.
        if (SystemUtils.isJavaVersionAtLeast(160)) {
            assertTrue("Two threads are expected to be deadlocked, but the following was found: "
                    + (ids == null ? "null" : Arrays.toString(ids)), ids != null && ids.length == 2);
        }
    }
}