五、Synchronized

一、使用(方法,代码块)

Synchronized锁就是基于对象实现的,只不过分为class对象和其他

  • this或者没有static修饰的方法,表示当前对象锁。只要用这个对象就被锁了,A线程访问S类的s1对象和B线程访问S类的S1对象,要竞争
  • .clss 和 static 表示类锁。只要你用这个类就牵扯类锁,比如A线程访问S类的s1对象和B线程访问S类的S2对象,一样要竞争
  • 普通对象,就表示锁了这个普通对象
  • 字符串算是类锁

代码块

public static void main(String[] args) {
   Object obj = new Object();
   synchronized (obj) {
       // ...
   }
}
synchronized (this) {
    // ...
}
synchronized (Test.class) {
    // ...
}

方法

private static synchronized void sync () {
    
}
private synchronized void  sync () {

}

二、优化(锁消除、锁粗化、锁升级)

jdk1.6以后,对synchronized做了锁消除,锁粗化、锁升级。要不然这个锁一直是重量级锁,就会导致没有人用了,浪费性能。用ReentrantLock不香吗?

锁消除

  • 就是当synchronized包裹的代码里面,没有临界资源或者说没有跟别的线程会有竞争的时候,这个锁是不存在的,就会把锁消除
  • jvm逃逸分析,这个锁对象会不会被多个线程共享,不会的话,就会把锁消除。

锁粗化

  • 当一串代码需要不停的需要这个锁的时候,就会浪费资源,还不如把这个锁的范围扩大
    在这里插入图片描述

锁升级

对象在jvm堆中的内存结构,对象头中的MarkWord如下
在这里插入图片描述

无锁

  • 就是没有锁

偏向锁/匿名偏向锁

  • 匿名偏向锁:只在对象的markword改变了锁状态,但是没有对象的地址。
  • 当只有一个线程竞争这个锁的时候,就会从无锁变为偏向锁。然后再锁对象的markword中记录这个线程的ID,以后这个线程来了,就继续用啊。
  • 偏向锁有一个默认时延4s。可以修改。就是程序启动后,不会立马有偏向锁,而是要等到4s后,为啥呢?
    • 因为hostspot加载类的时候,ClassLoder类中,加载类是synchronized的。而偏向锁向轻量级转化的时候,需要有一个安全点(STW)才能转化。但是你是启动啊,提升启动效率。
  • 默认是开启时延,且开启偏向锁的。那么这个时候,启动的时候不会立马有偏向锁。但是等时延过了之后,就不会有无锁的状态了,起码都是个匿名偏向锁。

轻量级锁

  • 当有多个线程竞争同一把锁的时候,偏向锁升级为轻量级锁。竞争的线程会在栈中开一个空间Lock Record用来复制锁对象的Mark Word,用自适应自旋的方式(CAS)来竞争这个锁。只要能将锁对象头中的指针改为自己,那么就是抢锁成功了

重量级锁

  • 底层是ObjectMonitor,这个玩意就是hotspot的c代码了。(有兴趣去研究一下,下面是在百度抄的,大概瞅瞅就行了)
ObjectMonitor() {
    _header       = NULL;   // header存储着对象的MarkWord
    _count        = 0;      // 竞争锁的线程个数
    _waiters      = 0,      // wait的线程个数
    _recursions   = 0;      // 标识当前synchronized锁重入的次数
    _object       = NULL;
    _owner        = NULL;   // 持有锁的线程
    _WaitSet      = NULL;   // 保存wait的线程信息,双向链表(等待池)
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;  // 获取锁资源失败后,线程要放到当前的单向链表中(锁池)
    FreeNext      = NULL ;
    _EntryList    = NULL ;  // _cxq以及被唤醒的WaitSet中的线程,在一定机制下,会放到EntryList中
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
}

TryLock

int ObjectMonitor::TryLock (Thread * Self) {
   for (;;) {
      // 拿到持有锁的线程
      void * own = _owner ;
      // 如果有线程持有锁,告辞
      if (own != NULL) return 0 ;
      // 说明没有线程持有锁,own是null,cmpxchg指令就是底层的CAS实现。
      if (Atomic::cmpxchg_ptr (Self, &_owner, NULL) == NULL) {
         // 成功获取锁资源
         return 1 ;
      }
      // 这里其实重试操作没什么意义,直接返回-1
      if (true) return -1 ;
   }
}

try_entry

bool ObjectMonitor::try_enter(Thread* THREAD) {
  // 在判断_owner是不是当前线程
  if (THREAD != _owner) {
    // 判断当前持有锁的线程是否是当前线程,说明轻量级锁刚刚升级过来的情况
    if (THREAD->is_lock_owned ((address)_owner)) {
       _owner = THREAD ;
       _recursions = 1 ;
       OwnerIsThread = 1 ;
       return true;
    }
    // CAS操作,尝试获取锁资源
    if (Atomic::cmpxchg_ptr (THREAD, &_owner, NULL) != NULL) {
      // 没拿到锁资源,告辞
      return false;
    }
    // 拿到锁资源
    return true;
  } else {
    // 将_recursions + 1,代表锁重入操作。
    _recursions++;
    return true;
  }
}

enter(想方设法拿到锁资源,如果没拿到,挂起扔到_cxq单向链表中)

void ATTR ObjectMonitor::enter(TRAPS) {
  // 拿到当前线程
  Thread * const Self = THREAD ;
  void * cur ;
  // CAS走你,
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
  if (cur == NULL) {
     // 拿锁成功
     return ;
  }
  // 锁重入操作
  if (cur == Self) {
     // TODO-FIXME: check for integer overflow!  BUGID 6557169.
     _recursions ++ ;
     return ;
  }
  //轻量级锁过来的。
  if (Self->is_lock_owned ((address)cur)) {
    _recursions = 1 ;
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }
​
​
  // 走到这了,没拿到锁资源,count++
  Atomic::inc_ptr(&_count);
    for (;;) {
      jt->set_suspend_equivalent();
      // 入队操作,进到cxq中
      EnterI (THREAD) ;
      if (!ExitSuspendEquivalent(jt)) break ;
      _recursions = 0 ;
      _succ = NULL ;
      exit (false, Self) ;
      jt->java_suspend_self();
    }
  }
  // count--
  Atomic::dec_ptr(&_count);
  
}

EnterI

for (;;) {
    // 入队
    node._next = nxt = _cxq ;
    // CAS的方式入队。
    if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;// 重新尝试获取锁资源
    if (TryLock (Self) > 0) {
        assert (_succ != Self         , "invariant") ;
        assert (_owner == Self        , "invariant") ;
        assert (_Responsible != Self  , "invariant") ;
        return ;
    }
}

锁降级

  • 其实是可以降级的,因为偏向锁没有地方存hashcode,当处于偏向锁的时候,需要使用对象的hashcode,那么就要从偏向锁降级到无锁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值