Java并发 lockInterruptibly导致IllegalMonitorStateException异常研究

前言

使用ReentrantLock过程中遇到IllegalMonitorStateException崩溃,于是研究一番。

复现

使用如下代码可以复现:

public class LockTest {
    public static void test() {
        Thread thread = new Thread(new TestRunnable());
        thread.start();

        // 加上sleep代码不会IllegalMonitorStateException
//        try {
//            Thread.sleep(1000);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
        thread.interrupt();
    }
    
    private static class TestRunnable implements Runnable {

        private static Lock lock = new ReentrantLock();

        @Override
        public void run() {
            try {
//                lock.lock(); // 没问题
                lock.lockInterruptibly(); // 抛出IllegalMonitorStateException

                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    // log
                }

            } catch (InterruptedException e) {
                // log
            } finally {
                lock.unlock();
            }
        }
    }
}

源码分析

public class ReentrantLock implements Lock, java.io.Serializable {
    abstract static class Sync extends AbstractQueuedSynchronizer {
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }

    static final class NonfairSync extends Sync {
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException(); // 抛出异常,getExclusiveOwnerThread为null
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
    }

    // 调用lock方法会去获取锁,直接获取或者在AQS里排队获取,不会判断线程状态
    public void lock() {
        sync.lock();
    }

    // 判断线程状态后再获取锁
    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    public void unlock() {
        sync.release(1);
    }
}

从源码可以看出

  • lock()方法会去获取锁,直接获取或者在AQS里排队获取,不会判断线程状态
  • lockInterruptibly() 判断线程状态后再获取锁

崩溃原因

导致崩溃的原因是调用thread.interrupt()太快了,ReentrantLock还在调用acquireInterruptibly时候该线程就已经interrupted了。判断Thread.interrupted()直接抛出InterruptedException。
然后finally语句在调用unlock释放锁时,exclusiveOwnerThread为null,因为在走进获取锁的流程前该线程已经interrupted了。
将测试代码中test方法加上sleep代码则不会IllegalMonitorStateException,因为那时候已经获取到锁了,exclusiveOwnerThread不为null。
Java并发 lockInterruptibly导致IllegalMonitorStateException异常研究_第1张图片

结论

如测试代码所示,该代码并没有线程竞争,只有一个线程也会有这种问题存在,更何况在多线程场景下使用;
所以更需要注意不要滥用lock.lockInterruptibly,尤其有thread.interrupt()调用情况下。
不过一般等待唤醒场景下都会添加thread.interrupt()以中断线程,以尽早结束线程,所以尽量避免使用lock.lockInterruptibly
因为synchronized不能被中断,使用了ReentrantLock了就意味着要调用thread.interrupt(),所以还是尽量不使用它吧;只是多获取一次锁,在后面的condition判断中还是会被中断,所以只能说问题不大吧,使用lock()就完事了。

你可能感兴趣的:(dalvik/art虚拟机,多线程,并发编程,IllegalMonitor,多线程,java)