`
xieyj
  • 浏览: 102763 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

小议偏向锁

阅读更多

  java SE6采用偏向锁以提高性能。

  个人理解,偏向锁能提高性能,其理论基础是建立在绝大部分时间内,需要同步的代码其实只是一个线程在运行,正因为是单个线程在运行,所以尽量采用轻量的代码以提高性能,原子操作那是肯定需要的,但不需要采用切换上下文这类的重型操作。如果是很多线程同时运行到同步代码地方,同步代码稍长的话,采用偏向锁反而影响性能。

   如果是我设计锁,首先会考虑在内存内放置锁的标志,通过原子操作(CAS--比较、设置,这两个是一个原子操作),如果没有线程获取锁,先设置锁的标志,如果别的线程已经获取锁了,先自旋(自旋是因为现在大部分是多CPU,速度很快,很多同步代码很短,执行很快,不需要进行较重的上下文切换),如果自旋一段时间后还是没有办法获取锁,则将线程加入等待队列中。这上面说起来简单,其实要实现并不容易,因为等待队列也是多线程操作,也需要同步,这本身就需要和设计的锁同样的原理。

   看看openjdk是如何设计偏向锁的。

   openjdk采用两个字节长的对象头作为锁,这个是在jvm分配的对象当中。这两个字节的意义如下:

 

 

   这个也就是其他篇中分析过的对象头(mark word)。

   上面的对象头是如何起作用的呢?

    首先,通过monitorenter字节码调用,如果当前对象没有加锁,则给这个对象加上lightweight锁,会在线程的堆栈中分配一个锁记录(lock record)。锁记录包括对象原来的mark word和对象锁的所有者,在一个锁请求的过程中,一个mark word会被拷贝到锁记录中,然后对对象的mark word和锁记录中的进行原子的比较和设置操作(CAS),如何成功,则当前线程拥有锁,失败则表示其他线程已经拥有这个锁,需要进行锁的inflated操作(由轻量锁升级到重量锁)。

    如果是使用偏向锁,在请求过程,会尝试CAS操作将线程ID设置到mark word中,如果成功,对象锁偏向于这个线程,如果失败,这个线程的偏向必须撤销。

   如果对象锁偏向于当前线程,则当前线程对这个对象头将不再作操作。

   看一下bytecodeInterpreter.cpp中对monitorenter进行解释的源码。

   CASE(_monitorenter): {
        oop lockee = STACK_OBJECT(-1);
        BasicObjectLock* limit = istate->monitor_base();
        BasicObjectLock* most_recent = (BasicObjectLock*) istate->stack_base();
        BasicObjectLock* entry = NULL;
        while (most_recent != limit ) {
          if (most_recent->obj() == NULL) entry = most_recent;
          else if (most_recent->obj() == lockee) break; //找到拥有这个对象锁的监视器
          most_recent++;
        }
        if (entry != NULL) {
          entry->set_obj(lockee);
          markOop displaced = lockee->mark()->set_unlocked();
          entry->lock()->set_displaced_header(displaced);
          if (Atomic::cmpxchg_ptr(entry, lockee->mark_addr(), displaced) != displaced) {//原子比较,如果当前没有加锁,将displaced word 拷贝到 record word
            //进入这里表示已经有线程获取锁了

            if (THREAD->is_lock_owned((address) displaced->clear_lock_bits())) { //如果是当前用户拥有这个锁,则加上轻量锁
              entry->lock()->set_displaced_header(NULL); //加上轻量锁
            } else { //加锁不成功,继续处理
              CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
            }
          }
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
        } else {
          istate->set_msg(more_monitors);
          UPDATE_PC_AND_RETURN(0); // Re-execute
        }
      }

   这个锁的偏向还是很复杂的,如果让其他线程进行等待也是一个关键的问题,有时间再做分析。附一篇偏向锁的论文供大家学习。

  • 大小: 13.7 KB
1
0
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics