Bug:StampedLock的中断问题导致CPU爆满

StampedLock作为JAVA8中出现的新型锁,很可能在大多数场景都可以替代ReentrantReadWriteLock。它对于读/写都提供了四个接口(换成write为写锁):

  • readLock()
  • tryReadLock()
  • tryReadLock(long time, TimeUnit unit)
  • readLockInterruptibly()

这几个方法对应的语义为:

  • 获取读锁(阻塞,不响应中断)
  • 获取读锁(立即)
  • 限时获取读锁(响应中断)
  • 获取读锁(阻塞,响应中断)

然而在readLock方法(即不响应中断)中存在问题(write的版本也是),观察CPU使用率,执行以下代码:
public class TestStampedLock {
    public static void main(String[] args) throws InterruptedException{
    final StampedLock lock = new StampedLock();
    new Thread(){
       public void run(){
       long readLong = lock.writeLock();
       LockSupport.parkNanos(6100000000L);
       lock.unlockWrite(readLong);
     }
    }.start();
    Thread.sleep(100);
    for( int i = 0; i < 3; ++i)
       new Thread(new OccupiedCPUReadThread(lock)).start();
    }
    private static class OccupiedCPUReadThread implements Runnable{
        private StampedLock lock;
        public OccupiedCPUReadThread(StampedLock lock){
            this.lock = lock;
        }
        public void run(){
            Thread.currentThread().interrupt();
            long lockr = lock.readLock();
            System.out.println(Thread.currentThread().getName() + " get read lock");
            lock.unlockRead(lockr);
        }
    }
}
先开启一个线程获取写锁并保持6秒,再开启三个带着中断状态的线程去获取读锁(readLock方法),结果是3个核心被占据了近6秒。
原因在于没有使用保存/复原中断状态的机制,通过hack源码,插入保存中断和返回前恢复中断的相关代码即可修复:
boolean interrupted = false;
if(interrupted)
    Thread.currentThread().interrupt();
return ns;
if(Thread.interrupted()){
    if(interruptible)
        return cancelWaiter(node, p, true);
    else
        interrupted = true;
}

原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Bug:StampedLock的中断问题导致CPU爆满


FavoriteLoading添加本文到我的收藏
  • Trackback 关闭
  • 评论 (8)
    • sunqi
    • 2014/07/10 4:28下午

    中断状态,很多时候确实被忽略了,之前也碰到tomcat nio+servlet异步,在中断状态下计数器泄漏问题,tomcat最新版也刚修复

    // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
      • JaonHui
      • 2014/07/10 5:26下午

      嗯,这些基础模块的实现还是要仔细考虑才行

      // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
  1. 如果作者解释下为什么没有使用“保存/复原中断状态”的机制就会造成cpu满负荷,读者可能更容易理解一些。
    我补充两句吧,因为实现等待的那部分逻辑在一个循环里,里面有一个LockSupport.pack来实现等待,满足条件后才跳出循环,结束等待,但如果线程处于中断状态,LockSupport.pack不会开始等待或继续等待,而且也不会清除线程的中断状态,所以造成了在循环里无限调用
    LockSupport.pack(pack总是立即返回)的情形,所以cpu就满负荷了

    // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
      • JaonHui
      • 2014/07/16 1:36下午

      对的,说的很清楚.

      // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
      • 貌似有点拆台的味道了,Sorry!
        我看文章的时候正好有此疑惑,自己弄明白后,就把它写下来了:)

        // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
          • JaonHui
          • 2014/07/17 5:59下午

          晕,不会呀,你只是就事论事呀,我没写出来是考虑到循环逻辑本身是不好理解的,所以重点给出了每个人都可以重现问题的代码。你这个评论也可以帮助其他读者理解,这很好的:)

          // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
    • Haha
    • 2014/07/21 3:51下午

    看了下,没明白呀。Thread.currentThread().interrupt();代码注释掉,也没什么

    // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
      • JaonHui
      • 2014/07/21 6:41下午

      Thread.currentThread().interrupt();不要注释掉,跑示例代码看CPU使用率,如果你电脑是4核的话CPU会持续75%左右。

      // Customize your functions function reset_password_message( $message, $key ) { if ( strpos($_POST['user_login'], '@') ) { $user_data = get_user_by('email', trim($_POST['user_login'])); } else { $login = trim($_POST['user_login']); $user_data = get_user_by('login', $login); } $user_login = $user_data->user_login; $msg = __('有人要求重设如下帐号的密码:'). "\r\n\r\n"; $msg .= network_site_url() . "\r\n\r\n"; $msg .= sprintf(__('用户名:%s'), $user_login) . "\r\n\r\n"; $msg .= __('若这不是您本人要求的,请忽略本邮件。') . "\r\n\r\n"; $msg .= __('要重置您的密码,请打开下面的链接:'). "\r\n\r\n"; $msg .= network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login') ; return $msg; } add_filter('retrieve_password_message', reset_password_message, null, 2);
您必须 登陆 后才能发表评论

return top