笔者在kafka重复消费exception一文中处理offset重复提交的问题后,在执行了一段时间后发现又出现了重复消费的问题,于是检查了生产日志,发现没有了CommitFailedException问题,于是根据重复消费的id具体查看,发现了一个异常,同步锁解锁异常,这个异常的抛出是因为当前线程释放了一个它并没有持有的锁,这种操作是非法的。
此次重复消费的问题大概就找出原因了,代码中出现了异常后往外抛出去了,kafka在消费的时候,认为消息没有正常消费,于是就让消费者再次消费,但是消费还是失败,就这样一直循环下去,导致消息消费越来越慢。
在检查代码的过程中,发现了同步锁释放锁的时候失去了锁对象,因为同步锁设置了过期时间,由于接口逻辑执行时间过长,锁已经失效了,在unlock的时候,锁不存在了,就报错了,于是修改同步锁释放锁资源的逻辑:在释放锁的时候判断当前前锁是否存在,并且查询当前线程是否保持此锁定。
示例:
if(lock.lock() && lock.isHeldByCurrentThread()) {
}
lock.lock()
在Java中,ReentrantLock是一个可重入的互斥锁,它与synchronized关键字类似,可以用来实现多线程间的互斥访问,防止数据竞争和线程安全问题。在使用ReentrantLock时,我们需要使用lock()方法来获取锁,以确保在执行临界区代码时,只有一个线程可以访问。
lock.lock()方法是ReentrantLock类中获取锁的方法,调用该方法会尝试获取锁。如果锁当前没有被其他线程持有,则获取锁并返回。如果锁已经被其他线程持有,则当前线程会被阻塞,直到获取锁为止。同时,如果当前线程在等待锁的过程中被中断,那么lock.lock()方法会抛出InterruptedException异常。
下面是一个简单的示例代码,展示了如何使用lock.lock()方法获取锁:
public class LockDemo {
private ReentrantLock lock = new ReentrantLock();
public void doSomething() {
lock.lock(); // 获取锁
try {
// 执行临界区代码
System.out.println(Thread.currentThread().getName() + " is running");
} finally {
lock.unlock(); // 释放锁
}
}
}
lock.isHeldByCurrentThread()
lock.isHeldByCurrentThread()是ReentrantLock类中的一个方法,用于判断当前线程是否持有锁。如果当前线程持有锁,则返回true;否则返回false。可以使用lock.isHeldByCurrentThread()方法来判断当前线程是否持有锁,从而避免出现不必要的错误。
下面是一个示例代码,展示了如何使用lock.isHeldByCurrentThread()方法判断当前线程是否持有锁:
public class LockDemo {
private ReentrantLock lock = new ReentrantLock();
public void doSomething() {
lock.lock(); // 获取锁
try {
// 判断当前线程是否持有锁
if (lock.isHeldByCurrentThread()) {
// 执行业务代码 ...
System.out.println(Thread.currentThread().getName() + " is running");
}
} finally {
lock.unlock(); // 释放锁
}
}
}