【锁】的相关概念汇总

本文汇总了关于锁的相关概念,包括死锁的四个条件及其避免策略,悲观锁和乐观锁的区别。通过实例分析了死锁的产生及解决办法,强调了锁在并发编程中的重要性,并探讨了Java中synchronized和ReentrantLock等锁的实现和优化策略。
摘要由CSDN通过智能技术生成

大厂面试---Redis面试题(含答案,相关知识点,面试考察次数统计)_安吉_lh1029的博客-CSDN博客

大厂面试问答题汇总分析---数据库(索引-聚集/非聚集,事务,mySql, 锁)_安吉_lh1029的博客-CSDN博客

大厂面试问答题汇总分析---网络安全类问题_安吉_lh1029的博客-CSDN博客

大厂面试问答题汇总分析--- 秒杀 / 限流 / 高并发_安吉_lh1029的博客-CSDN博客

【锁】的相关概念汇总_安吉_lh1029的博客-CSDN博客

大厂面试问答题汇总分析--- 多线程问题_安吉_lh1029的博客-CSDN博客

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现。如 synchronized 和 ReentrantLock(并发包中的锁类)等 。

1、死锁

线程之间相互等着对方释放资源,而自己的资源又不释放给别人,这种情况就是死锁。所以,只要其中一线程释放了资源,死锁就会被解除。

1-1、死锁的4个条件:

互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

避免:

1)尽量避免使用多个锁,并且只有需要时才持有锁定位死锁,嵌套的 synchronized 或者 lock 非常容易出问题。
2)如果必须使用多个锁,尽量设计好锁的获取顺序
3)使用带超时的方法,为程序带来更多可控性。Object.wait(…) 或者 CountDownLatch.await(…),都支持所谓的 timed_wait。
4)尽量不要几个功能用同一把锁。

1-2、写一个死锁的案例

public class Deadlock{
    public static void main(String[] args){
         Test thread1 = new Test(1,"线程1");
         Test thread2 = new Test(2,"线程2");
         thread1.start();
         thread2.start();
    
    }
}

class Test extends Thread{
     //需要的资源只有一份,用static来保证只有一份
     static Object lockA = new Object();
     static Object lockB = new Object();

     //变量,便于打印输出
     private int flag;
     private String threadName;
     Test(int value, String name){
       this.flag = value;
       this.threadName = name;
     }

     @Override
     public void run(){
         System.out.println("flag ="+ flag);
         if(flag == 1){
             synchronized(lockA){
                 System.out.println(threadName +"获得lockA的锁");
                 try {
                     Thread.sleep(1000);
                 }catch (InterruptedException e)  {
                      e.printStackTrace();
                 }
                 synchronized(lockB){
                     System.out.println(threadName +"获得lockB的锁");
                 }
             }
         }else{
             synchronized(lockB){
                 System.out.println(threadName +"获得lockB的锁");
                 try {
                     Thread.sleep(1000);
                 }catch (InterruptedException e)  {
                      e.printStackTrace();
                 }
                 synchronized(lockA){
                     System.out.println(threadName + "获得 lockA的锁");
                 }
             }
         }
     }

}

上面这个案例死锁原因

thread1在获取lockA的同时还想获取lockB

thread2在获取lockB的同时还想获取lockA

因此造成死锁,输出内容为

 解除上文中的死锁,就是当thread1获取完lockA后释放,再获取lockB, 在上文中代码变更如下即可

synchronized(lockA){
     System.out.println(threadName +"获得lockA的锁");
     try {
         Thread.sleep(1000);
     }catch (InterruptedException e)  {
         e.printStackTrace();
     }
}
synchronized(lockB){
     System.out.println(threadName +"获得lockB的锁");
}

此时就不会造成死锁了,输出内容如下:

监视锁monitor :是每个对象都有的一个隐藏字段。申请锁成功之后,monitor就会成为当前线程的唯一持有者。线程第一次执行monitorenter指令后,monitor的值由0变为1。当该线程再次遇到monitorenter指令后,就会将monitor继续累加1。这也是synchronized实现重入锁的原理。

总结:

同步操作的实现,需要给对象关联一个互斥体,这个互斥体就可以叫做锁。

锁的作用是,保证同一竞争资源在同一时刻只会有一个线程占有。

Java中锁的实现方式有两种:synchronized关键字和并发包中的锁类。

锁的优化策略有:锁消除、锁偏向、自适应自旋锁、锁粗化。

尽量不要在循环内使用锁,以减少资源消耗。

悲观锁 和 乐观锁 区别

1.悲观锁是当线程拿到资源时,就对资源上锁,并在提交后,才释放锁资源,其他线程才能使用资源。

2.乐观锁是当线程拿到资源时,上乐观锁,在提交之前,其他的锁也可以操作这个资源,当有冲突的时候,并发机制会保留前一个提交,打回后一个提交,让后一个线程重新获取资源后,再操作,然后提交。和git上传代码一样,两个线程都不是直接获取资源本身,而是先获取资源的两个copy版本,然后在这两个copy版本上修改。

3.悲观锁和乐观锁在并发量低的时候,性能差不多,但是在并发量高的时候, 乐观锁的性能远远优于悲观锁。

4.我们常用的synchronized是悲观锁,lock是乐观锁 。他们之间的区别详见于Java同步锁——lock与synchronized 的区别【转】

5.还有一个乐观锁的例子 https://www.cnblogs.com/darrenqiao/p/9211447.html

参考:

悲观锁和乐观锁的区别 - Mop猎人 - 博客园

Java中锁种类、实现方式_炎升的博客-CSDN博客_java锁的实现方式

几率大的多线程面试题(含答案)__睶_的博客-CSDN博客_几率大的多线程面试题

死锁的代码实现_野火堆的博客-CSDN博客_死锁代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值