已解决java.lang.IllegalMonitorStateException: null,亲测有效,嘿嘿嘿

java.lang.IllegalMonitorStateException 异常通常表示线程试图对一个对象进行解锁,但它并没有持有该对象的锁。换句话说,这个异常通常是因为同步代码块的使用不当,或者试图对一个没有被当前线程锁定的对象进行 wait(), notify(), 或 notifyAll() 操作。

问题分析

当你看到 IllegalMonitorStateException 时,这通常意味着你在编写多线程代码时,同步的使用方式不正确。可能的情况包括:

  1. 调用 wait(), notify(), 或 notifyAll() 的线程没有持有对象的锁。
  2. 调用 unlock() 的线程没有先调用 lock() 获得锁。
  3. synchronized 块之外尝试解锁。

报错原因

报错的原因通常是同步代码块或方法的使用不当。比如,你可能在没有获得锁的情况下尝试释放锁,或者在错误的对象上调用 wait(), notify(), 或 notifyAll()

解决思路

解决这个问题的关键是确保:

  1. 只有在持有对象锁的情况下,才调用 wait(), notify(), 或 notifyAll()
  2. 使用 Lock 接口时,确保在调用 unlock() 之前先调用 lock()
  3. 同步代码块应正确地包裹需要同步的代码。

代码示例1

以下是针对这三个解决思路增加的代码示例:

1. 只有在持有对象锁的情况下,才调用 wait(), notify(), 或 notifyAll()

public class SynchronizationExample {
    private final Object lock = new Object();

    public void waitForSignal() throws InterruptedException {
        synchronized (lock) {
            // 等待通知,当前线程必须持有lock对象的锁
            lock.wait();
            // 收到通知后继续执行
            System.out.println("Received signal, continuing execution...");
        }
        // 退出同步块后,自动释放lock对象的锁
    }

    public void sendSignal() {
        synchronized (lock) {
            // 发送通知,当前线程必须持有lock对象的锁
            lock.notify();
            System.out.println("Signal sent.");
        }
        // 退出同步块后,自动释放lock对象的锁
    }
}

2. 使用 Lock 接口时,确保在调用 unlock() 之前先调用 lock()

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();

    public void doSomethingWithLock() {
        lock.lock(); // 获取锁
        try {
            // 临界区,只有获取到锁的线程才能执行这里的代码
            System.out.println("Doing something important...");
        } finally {
            lock.unlock(); // 释放锁,无论是否发生异常
        }
    }
}

3. 同步代码块应正确地包裹需要同步的代码

public class CorrectSynchronization {
    private int counter = 0;

    public synchronized void incrementCounter() {
        // 同步方法,自动使用this作为锁对象
        counter++;
        System.out.println("Counter incremented to " + counter);
    }

    public void incrementCounterWithBlock() {
        synchronized (this) {
            // 同步代码块,显式指定锁对象this
            counter++;
            System.out.println("Counter incremented to " + counter + " in block");
        }
        // 退出同步代码块后,自动释放this对象的锁
    }
}

在第一个示例中,waitForSignalsendSignal 方法使用 synchronized 块和 wait/notify 来同步线程。调用 waitnotify 的线程必须持有 lock 对象的锁。

在第二个示例中,doSomethingWithLock 方法使用 Lock 接口的 lockunlock 方法来控制对临界区的访问。lock.lock() 必须在尝试访问共享资源之前调用,而 lock.unlock() 则在访问结束后调用,以释放锁。

在第三个示例中,incrementCounter 方法是一个同步方法,它使用 this 作为锁对象来同步对 counter 的访问。incrementCounterWithBlock 方法则显式地使用 synchronized 块和 this 作为锁对象来同步对 counter 的访问。这两种方式都确保了在任意时刻只有一个线程能够修改 counter 的值。

代码示例2

public class Example {
    private Object lock = new Object();

    public void someMethod() {
        synchronized (lock) {
            // ... do something ...
        }
        
        // 错误的尝试释放锁,因为当前线程不再持有lock的锁
        lock.notify();
    }
}

要修复这个问题,我们需要确保在调用 notify() 时,当前线程持有 lock 的锁。下面是修复后的代码示例:

public class Example {
    private Object lock = new Object();

    public void someMethod() {
        synchronized (lock) {
            // ... do something ...
            
            // 正确的释放锁前的通知,因为当前线程持有lock的锁
            lock.notify();
        }
        // 锁在这里被自动释放,因为离开了synchronized块
    }
}

在这个修复后的例子中,notify() 调用被放在了 synchronized 块内部,这样当 notify() 被调用时,当前线程就持有 lock 的锁。当线程退出 synchronized 块时,锁会被自动释放。

记住,任何时候调用 wait(), notify(), 或 notifyAll(),你都应该确保它们被包裹在一个 synchronized 块中,并且该块使用正确的对象作为锁。如果你使用 Lock 接口和它的 lock()unlock() 方法,同样要确保正确的锁定和解锁顺序。

  • 14
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值