JAVA中锁基本认知

JAVA中锁基本认知

1.宏观上大致分为悲观锁和乐观锁
悲观锁
悲观锁认为读少写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会阻塞直到拿到锁。
乐观锁
乐观锁认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,在写之前先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。
java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。

2.java线程阻塞的坏处
java线程是映射到操作系统原生线程上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态和核心态之间切换,会消耗大量系统资源。
如果线程状态切换频繁的话,会消耗cpu很多处理时间。
如果对于需要同步的简单代码块,获取锁挂起操作消耗的时间比用户执行代码消耗的时间还长的话,是很糟糕的事情。
synchronized会导致争用不到锁的线程进入阻塞状态,所以说它是java语言中一个重量级的同步操纵,被称为重量级锁。

3.JAVA中的锁
悲观锁:重量级锁
乐观锁:轻量级锁,自旋锁,偏向锁

自旋锁
锁的线程能在很短时间内释放锁资源,等待竞争锁的线程不需要做内核态和用户态之间的切换进入阻塞挂起状态,只需要等一等(自旋),等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
但是线程自旋需要消耗cpu,如果一直获取不到锁,竞争锁的线程就会一直消耗cpu,所以一般要设定自旋等待最大时间。
如果持有锁的线程超过自旋等待最大时间还是没有释放锁,导致其他争用锁的线程获取不到锁,这时争用线程就会停止并进入阻塞状态。

重量级锁Synchronized
Synchronized可以把任意一个非null的对象当作锁。
1)作用于方法时,锁住的是对象的实例(this);
2)当作用于静态方法时,锁住的是Class实例,又因为Class的相关数据存储在永久带,因为永久带是全局共享的,因此静态方法锁相当于类的一个全局锁,会锁所有调用该方法的线程。
3)synchronized作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。
Synchronized是非公平锁。 Synchronized在线程进入ContentionList时,等待的线程会先尝试自旋获取锁,如果获取不到就进入ContentionList,这明显对于已经进入队列的线程是不公平的,还有一个不公平的事情就是自旋获取锁的线程还可能直接抢占OnDeck线程的锁资源。

偏向锁
偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。
如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。

轻量级锁
轻量级锁是由偏向锁升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级。

锁优化
1)减少锁的时间
不需要同步执行的代码,能不放在同步代码块里面执行就不要放在同步代码块内,可以让锁尽快释放。
2)减少锁的粒度
它的思想是将物理上的一个锁,拆成逻辑上的多个锁,增加并行度,从而降低锁竞争。它的思想是用空间来换时间。
3)锁粗化
假如有一个循环,循环内的操作需要加锁,我们应该把锁放到循环外面,否则每次进出循环,都进出一次临界区,效率是非常差的。
4)使用读写锁
ReentrantReadWriteLock 是一个读写锁,读操作加读锁,可以并发读,写操作使用写锁,只能单线程写。

整理借鉴了很多大佬写的,在此无法一一说明,这只是个人用来查漏补缺的文章,如果对你有帮助我很高兴。

4.synchronized与Lock的区别
ReentrantLock是Lock接口的实现。
1)synchronized是java的关键字,在jvm层面上,而lock是个接口。
2)synchronized获取锁的线程执行完同步代码,释放锁,线程执行发生异常,jvm会让线程释放锁,而lock在finally中必须释放锁,不然容易造成线程死锁。
3)synchronized假设a线程获得锁,b线程等待,如果a线程阻塞,那么b线程会一直等待。而lock锁有多个获取锁的方式,线程可以不用一直等待。
4)synchronized中锁的状态无法判断。而lock中锁的状态可以判断。

5.死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方的资源,从而导致恶性循环的现象。
死锁产生的四个必要条件
1)互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
2)不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
3)请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
4)循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

死锁的解决方案
1)如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
2)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率。
3)对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。

整理借鉴了很多大佬写的,在此无法一一说明,这只是个人用来查漏补缺的文章,如果对你有帮助我很高兴。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值