什么是自旋锁

  当一个线程尝试去获取某一把锁的时候,如果这个锁已经被另外一个线程占有了,

  那么此线程就无法获取这把锁,该线程会等待,间隔一段时间后再次尝试获取。

  这种采用循环加锁,等待锁释放的机制就称为自旋锁(spinlock)

为什么需要自旋锁

  由于在多处理器的环境中某些资源的有限性,有时需要互斥访问,

  这时候就需要引入锁的概念,只有获取到锁的线程才能对临界资源进行访问,

  由于多线程的核心是CPU的时分片,所以同一时刻只能又一个线程获取到锁。

  但是那些没有获取到锁的线程该怎么办呢?

  通常有两种做法:

  • 一种是没有获取到锁的线程就一直等待判断该资源是否已经释放了锁,
    这种锁叫做自旋锁,它不会引起线程阻塞(本质上是一种忙等待机制,避免线程切换带来的系统开销)。
  • 一种是,没有获取到锁的线程把自己阻塞起来,重新等待CPU的调度,这种锁称为互斥锁

自旋锁的原理

  自旋锁实现的原理比较简单,当那些不能立马获取到锁资源的线程,

  它们不会像互斥锁那样直接将自己挂起进入阻塞状态,而是等一会(自旋)不判断的去判断锁资源是否被释放了,

  如果释放了那么就去获取锁资源。这样做避免了那些锁竞争不激烈的情况下,

  从核心态到用户态的切换,避免了系统上下文切换的开销。

  因为自旋锁避免了操作系统进程调度的和线程的切换,所以自旋锁通常适用于在时间比较短的情况下。

  但是如果长时间上锁的话,自旋锁是非常消耗性能的,因为它阻止了其他线程调度。

  如果线程持有锁的时间很长,那么其他线程将一直保持旋转状态(不断的去判断锁资源是否被释放了,并没有让出CPU)。

  为了解决上面的问题,可以给自旋锁加一个自旋的时间,等到时间一到立即释放自旋锁。

  自旋锁的目的是占有CPU资源,等到获取锁立即进行处理,但是如何去选择自旋的时间呢。

  如果自旋时间太长,会有大量的线程占用CPU,导致系统性能降低。

  在JDK1.6之后,引入了适应性自旋锁,意味这自旋的时间是不固定的,

  而是由前一次在同一个锁上自旋的时间以及锁拥有的状态来决定,基本认为线程上下文切换的时间是最佳的一个自旋时间。

自旋锁的优缺点

优点:

  在线程竞争不激烈的时候,自旋锁是需要等待前面一个获的锁的线程释放锁后

  就可以在很短的时间内获取到锁从而继续执行,

  避免了直接将线程阻塞再去重新等待CPU调度所浪费的两次上下文切换的系统开销,这可以极大的提升系统的性能

缺点:

  在线程竞争很激烈的时候,自旋锁就显得有一点笨拙了。

  因为自旋锁在获取到锁资源之前CPU是一直在做无用功,同时大量的线程去竞争一个锁资源,

  会导致获取锁的时间很长。这种情况下,就白白的浪费了许多CPU资源。

锁升级

  就是将众多 细粒度锁 转换为较少的 粗粒度的锁 以削减系统开销的进程。
比如(mysql):

  • 把行锁升级为表锁。

例子:

  假设一张表有300,0000个页数据,每个页大约有100条记录,那么总共有30000,0000条记录。
  若有一个事务执行全表更新的SQL语句,则需要对所有记录加表锁。
  若根据每行记录产生锁对象进行加锁,并且每个锁占用10字节,则仅对锁管理就需要差不多需要3GB的内存。
  而InnoDB存储引擎根据表进行加锁,并采用位图方式,
  假设每个页存储的锁信息占用30个字节,则锁对象仅需90MB的内存。由此可见两者对于锁资源开销的差距之大。

悲观锁、乐观锁

悲观锁:

  每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。

乐观锁:

  每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会上锁。

悲观锁 和 乐观锁的区别

  乐观锁适用于读多写少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。

  如果经常产生冲突,上层应用会不断的进行,这样反倒是降低了性能

 

posted @ 2023-07-10 14:12  丶Ronnie  阅读(138)  评论(0)    收藏  举报