关于锁的认知探究

关于锁的认知探究

临界资源,临界区:每个进程中访问临界资源的那段代码称为临界区。若能保证诸进程互斥地进入自己的临界区,
便可实现诸进程对临界资源的互斥访问。为此,每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,
看它是否正被访问。如果此刻该临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问的标志;
如果此刻该临界资源正被某进程访问,则本进程不能进入临界区。在操作系统中,有临界区的概念。
临界区内放的一般是被1个以上的进程或线程(以下只说进程)共用的数据。临界区内的数据一次只能同时被一个进程使用,
当一个进程使用临界区内的数据时,其他需要使用临界区数据的进程进入等待状态。操作系统需要合理的分配
临界区以达到多进程的同步和互斥关系,如果协调不好,就容易使系统处于不安全状态,甚至出现死锁现象。
互斥锁:
重量级锁:当线程访问临界资源后,对资源进行互斥量标记,当有其他的线程想对该资源进行访问的时候发现已有互斥量,
会进行阻塞,直到访问临界资源的线程释放临界资源的互斥量,并唤醒阻塞的线程。唤醒进程可以唤醒单个进程和所有进程,
单个环境效率更高,但有条件即所有等待的线程都是在执行相同任务。且唤醒线程由条件变量决定,当某个条件变量没有
等待它的进程则可以进行销毁。---------------该解释来自linux系统
乐观锁:如果使用锁就要进行操作系统层面的阻塞和唤醒。这显得有点笨重,如果后继线程在知道互斥量已经锁定后,
一直对临界资源进行访问,形成概念上的“自旋”,这样线程一直处于运行的线程中(告诉缓存的线程),那他将更有效率。
但过度乐观未必是一件好事,访问某一个临界资源的线程很多的时候,一个进程锁定了互斥量,其他进程都进行自旋,
这样将有大量的线程在浪费资源,所以在自旋的同时,线程应该对所有对目标临界资源有企图的线程进行判断,
当竞争很激烈的时候,应该做适当的阻塞才是合理的。
AQS:AQS全称为AbstractQueuedSynchronizer,翻译过来就是抽象队列同步器。AQS是一个用来构建锁和其他同步
组件的基础框架
AQS核心思想是:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置
为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS
是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
CLH(Craig,Landin,and Hagersten)队列:是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点
之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配
AQS定义两种资源共享方式
* Exclusive(独占):只能有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
    * 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
    * 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
* Share(共享):多个线程可同时执行,如Semaphore、CountDownLatch等。
可重入锁:某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。只有释放该锁的计数器为0的时候,
才算真正的释放了该锁。synchronized和ReentrantLock都是可重入锁
可重入锁场景:假如我的某个事件有两个方法,都是用了一个锁,可重入锁允许我同时进入两个方法,完成方法的
时候再依次释放两个方法的锁。
如果实例对象存储在堆区时:实例对象内存存在堆区,实例的引用存在栈上,实例的元数据class存在方法区或者元空间;
公平锁与非公平锁:ReentrantLock有两者的实现,非公平锁会使用tryAcquire方法尝试获取锁
synchronized:
偏向锁:
Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。
偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在
多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢
复到标准的轻量级锁。
轻量级锁
轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁
就会升级为轻量级锁;
轻量级锁的加锁过程:
由轻量锁切换到重量锁,是发生在轻量锁释放锁的期间,之前在获取锁的时候它拷贝了锁对象头的markword,在释放锁
的时候如果它发现在它持有锁的期间有其他线程来尝试获取锁了,并且该线程对markword做了修改,两者比对发现不一致,
则切换到重量锁。
1、偏向锁(单线程访问的场景)
  在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到
  一些CAS操作,耗时)的代价而引入偏向锁。偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,
  此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,
  这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。
  对于锁竞争比较激烈的场合,就不能使用偏向锁了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这
  种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,先升级为轻量级锁。
2、轻量级锁(线程的竞争不激烈,线程交替执行)
  倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段(1.6之后加入的),
  此时Mark Word 的结构也变为轻量级锁的结构。轻量级锁能够提升程序性能的依据是 “对绝大部分的锁,在整个同步
  周期内都不存在竞争”,注意这是经验数据。需要了解的是,轻量级锁所适应的场景是线程交替执行同步块的场合,如果
  存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。


3、自旋锁
自旋不会丢弃CPU的使用权。
  轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。这是基于
  在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统
  实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,
  因此自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是
  称为自旋的原因),一般不会太久,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入临界区。
  如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式,这种方式确实也是可以提升效率的。
  最后没办法也就只能升级为重量级锁了。
  自适应自旋:JDK1.7之后,JVM会根据上次拿到锁自旋的次数去进行调整。若第一次自旋的次数为10还没有拿到,
  则第二次可能会调整为8;若第一次自旋次数为10就拿到了,则第二次的自旋次数可能调整为13;

好文地址:https://www.cnblogs.com/JonaLin/p/11571482.html

volatile修饰的内存.png
而volatile关键字解决的问题就是:当一个线程写入该值后,另一个线程读取的必定是新值。
volatile保证了修饰的共享变量在转换为汇编语言时,会加上一个以lock为前缀的指令,当CPU发现这个指令时,
立即会做两件事情:
1. 将当前内核中线程工作内存中该共享变量刷新到主存;
2. 通知其他内核里缓存的该共享变量内存地址无效 再读取该变量值的时候就需要重新从读取主内存中的值。
有序性:禁止指令重排,保证程序执行顺序。深入理解还未达到
原子性:volatile并不能保证方法的原子性,即非原子性操作使用volatile修饰的变量不会达到多线程安全结果,
类似i++

volatile是并发编程中的一种优化,在某些场景下可以代替Synchronized。但是,volatile的不能完全取代
Synchronized的位置,只有在一些特殊的场景下,才能适用volatile。总的来说,必须同时满足下面两个条件
才能保证在并发环境的线程安全:
  (1)对变量的写操作不依赖于当前值。
  (2)该变量没有包含在具有其他变量的不变式中。
参考:https://www.cnblogs.com/paddix/p/5428507.html
https://www.jianshu.com/p/ef70563fe726
乐观锁:乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,
所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,
让用户决定如何去做
悲观锁:当要对数据库中的一条数据进行修改的时候,为了避免同时被其他人修改,最好的办法就是直接对该数据进行
加锁以防止并发
CAS:比较替换
CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。
cas是乐观锁,经典的案例是数据库的读写实现,数据库通常会进行读改操作,当有两个进程同时要修改某个值的时候,
第一个修改已经对数据进行更改,由于没有加锁第二个进程也可以对同一个结果进行修改,但是会进行ABA验证,
当他发现ABA不成立的时候,就会撤销此次操作,重新进行。
CAS是一个判断替换操作,执行的是内存,成功执行或者执行失败,所以它是原子行为。
参考:
https://lwn.net/Articles/401911/
http://xianzilei.cn/blog/67
https://www.cnblogs.com/-jn-blog/articles/13163355.html
美团锁机制好文:https://tech.meituan.com/2018/11/15/java-lock.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值