您的位置:首页 > 科技 > IT业 > 网页设计旅游网站源代码免费_长沙计算机培训机构哪家最好_培训机构网站设计_谷歌关键词工具

网页设计旅游网站源代码免费_长沙计算机培训机构哪家最好_培训机构网站设计_谷歌关键词工具

2024/11/18 4:18:09 来源:https://blog.csdn.net/weixin_43558052/article/details/142905037  浏览:    关键词:网页设计旅游网站源代码免费_长沙计算机培训机构哪家最好_培训机构网站设计_谷歌关键词工具
网页设计旅游网站源代码免费_长沙计算机培训机构哪家最好_培训机构网站设计_谷歌关键词工具

锁的7大分类

偏向锁/轻量级锁/重量级锁

偏向锁:如果自始至终,对于这把锁都不存在竞争,那么其实就没必要上锁,只需要打个标记就行了,这就是偏向锁的思想。

一个对象被初始化后,还没有任何线程来获取它的锁时,那么它就是可偏向的,当有第一个线程来访问它并尝试获取锁的时候,它就将这个线程记录下来,以后如果尝试获取锁的线程正是偏向锁的拥有者,就可以直接获得锁,开销很小,性能最好

轻量级锁:JVM开发者发现在很多情况下,synchronized中的代码是被多个线程交替执行的,而不是同时执行的,也就是说并不存在实际的竞争,或者是只有短时间的锁竞争,用CAS就可以解决,这种情况下,用完全互斥的重量级锁是没必要的。

轻量级锁是指当锁原来是偏向锁的时候,被另一个线程访问,说明存在竞争,那么偏向锁就会升级为轻量级锁,线程会通过自旋的形式尝试获取锁,而不会陷入阻塞.

重量级锁:重量级锁是互斥锁,它是利用操作系统的同步机制实现的,所以开销相对比较大。当多个线程直接有实际竞争,且锁竞争时间长的时候,轻量级锁不能满足需求,锁就会膨胀为重量级锁。重量级锁会让其他申请却拿不到锁的线程进入阻塞状态。

锁的升级路径

可重入锁/非可重入锁

可重入锁:指的是线程当前已经持有这把锁了能在不释放这把锁的情况下再次获取这把锁

最典型的就是ReentrantLock了reentrant的意思就是可重入它也是Lock接口最主要的一个实现类

**非可重入锁:**指的是虽然线程当前持有了这把锁,但是如果想再次获取这把锁,也必须要先释放锁后才能再次尝试获取

共享锁/独占锁

**共享锁(读锁):**指的是同一把锁,可以被多个线程同时获得

**独占锁(写锁):**指的是这把锁只能同时被一个线程获得

公平锁/非公平锁

**公平锁:**公平的含义在于如果线程现在拿不到这把锁那么线程就都会进入等待,开始排队。在等待队列里等待时间长的线程会优先拿到1这把锁,有先来先得的意思

**非公平锁:**会在一定情况下忽略掉已经在排队的线程,发生插队现象

悲观锁/乐观锁

**悲观锁:**在获取资源之前必须先拿到锁,以便达到“独占”的状态。当前线程在操作资源的时候,其他线程由于不能拿到锁,所以其他线程不能来影响我

**乐观锁:**并不要求在获取资源前拿到锁,也不会锁住资源。相反,乐观锁利用CAS理念,在不独占资源的情况下,完成了对资源的修改

自旋锁/非自旋锁

**自旋锁:**如果线程现在拿不到锁,并不直接陷入阻塞或者释放CPU资源,而是开始利用循环,不停地尝试获取锁,这个循环过程被形象地比喻为“自旋”,就像是线程在“自我旋转”

**非自旋锁:**没有自旋的过程,如果拿不到锁就直接放弃,或者进行其他的处理逻辑,例如去排队、陷入阻塞等

可中断锁/不可中断锁

可中断锁:ReentrantLock是一种典型的可中断锁。例如使用locklnterruptibly方法在获取锁的过程中,突然不想获取了,那么也可以在中断。之后去做其他的事情不需要一直傻等到获取到锁才离开

**不可中断锁:**在Java中synchronized关键字修饰的锁代表的是不可中断锁,一旦线程申请了锁,就没有回头路只能等到拿到锁以后才能进行其他的逻辑处理

悲观锁与乐观锁

悲观锁

它认为如果不锁住这个资源,别的线程就会来争抢,就会造成数据结果错误所以悲观锁为了确保结果的正确性,会在每次获取并修改数据时,都把数据锁住让其他线程无法访问该数据,这样就可以确保数据内容万无一失

乐观锁

认为自己在操作资源的时候不会有其他线程来干扰,所以并不会锁住被操作对象,不会不让别的线程来接触它,同时为了确保数据正确性在更新前会去对比在我修改数据期间,数据有没有被其他线程修改过:

  • 如果没被修改过,就说明真的只有我自已在操作,那我就可以正常的修改数据
  • 如果发现数据和我一开始拿到的不一样了,说明其他线程在这段时间内修改过数据,那说明我迟了一步所以我会放弃这次修改,并选择报错、重试等策略

经典案例

  • 悲观锁:synchronized 关键字和 Lock接口

Java中悲观锁的实现包括synchronized关键字和Lock相关类等

例如Lock的实现类ReentrantLock,类中的lock()等方法就是执行加锁,而unlock()方法是执行解锁

处理资源之前必须要先加锁并拿到锁,等到处理完了之后再解开锁,这就是非常典型的悲观锁思想

  • 乐观锁:原子类

乐观锁的典型案例就是原子类

例如Atomiclnteger在更新数据时,就使用了乐观锁的思想,多个线程可以同时操作同一个原子变量

  • 大喜大悲:数据库

数据库中同时拥有悲观锁和乐观锁的思想

例如,我们如果在MySQL选择selectforupdate语句,那就是悲观锁,在提交之前不允许第三方来修改

该数据,这当然会造成一定的性能损耗,在高并发的情况下是不可取的

两种锁各自的使用场景

  • 悲观锁

适用于并发写入多、临界区代码复杂、竞争激烈等场景,这种场景下悲观锁可以避免大量的无用的反复尝试等消耗

  • 乐观锁

适用于大部分是读取,少部分是修改的场景,也适合虽然读写都很多但是并发并不激烈的场景。在这些场景下,乐观锁不加锁的特点能让性能大幅提高

如果看到synchronized背后的“monitor锁”

获取和释放monitor锁的时机

每个Java对象都可以用作一个实现同步的锁,这个锁也被称为内置锁或monitor锁。获得monitor锁的唯一途径就是进入由这个锁保护的同步代码块或同步方法,线程在进入被synchronized保护的代码块之前,会自动获取锁并且无论是正常路径退出,还是通过抛出异常退出,在退出的时候都会自动释放锁

用javap命令可以查看反汇编的结果

monitorenter

执行monitorenter的线程尝试获得monitor的所有权,会发生以下这三种情况之一:

  1. 如果该monitor的计数为0,则线程获得该monitor并将其计数设置为1,然后,该线程就是这个monitor的所有者
  2. 如果线程已经拥有了这个monitor,则它将重新进入,并且累加计数
  3. 如果其他线程已经拥有了这个monitor,那个这个线程就会被阻塞,直到这个monitor的计数变成为0,代表这个monitor已经被释放了,于是当前这个线程就会再次尝试获取这个monitor
monitorexit

monitorexit的作用是将monitor的计数器减1,直到减为0为止,代表这个monitor已经被释放了,已经没有任何线程拥有它了,也就代表着解锁,所以,其他正在等待这个monitor的线程,此时便可以再次尝试获取这个monitor的所有权

同步方法

被synchronized修饰的方法会有一个ACC_SYNCHRONIZED标志

synchronized和Lock

相同点

  • synchronized和Lock都是用来保护资源线程安全的
  • 都可以保证可见性,也就是说各个线程之前都知道方法或者代码块已经被加锁。
  • Synchronized和ReentrantLock都拥有可重入的特点

不同点

  • 用法不同

synchronized关键字可以加在方法上,不需要指定锁对象(此时的锁对象为this),也可以新建一个同步代码块并且自定义monitor锁对象。

而Lock接口必须显示用Lock锁对象开始加锁lock()和解锁unlock(),并且一般会在finally块中确保用unlock()来解锁以防发生死锁

  • 加锁顺序不同

lock可以不用考虑加锁解锁顺序,synchronized如果是obj1先加锁,然后obj2加锁,解锁的顺序就必须是obj2先解锁,然后在obj1再解锁。

  • synchronized锁不够灵活

一旦synchronized锁已经被某个线程获得了,此时其他线程如果还想获得,那它只能被阻塞,直到持有锁的线程运行完毕或者发生异常从而释放这个锁

  • synchronized锁只能同时被一个线程拥有,Lock锁没有这个限制。

例如Lock中的读锁可以被多个线程所持有。

  • 原理区别

synchronized是内置锁,由JVM实现获取锁和释放锁的原理,还分为偏向锁、轻量级锁、重量级锁。

  • 是否可以设置公平/非公平

公平锁是指多个线程在等待同一个锁时,根据先来后到的原则依次获得锁。

ReentrantLock等Lock实现类可以根据自己的需要来设置公平或非公平,synchronized则不能设置。

  • 性能区别

Java5以及之前,synchronized的性能比较低

Java6以后,因为JDK对synchronized进行了很多优化,比如自适应自旋、锁消除、锁粗化、轻量级锁、偏向锁等,所以后期的Java版本里的synchronized的性能并不比Lock差

如何选择

  1. 如果能不用最好既不使用Lock也不使用synchronized。因为在许多情况下你可以使用java.util.concurrent包中的机制,它会为你处理所有的加锁和解锁操作,也就是推荐优先使用工具类来加解锁
  2. 如果synchronized关键字适合你的程序,那么请尽量使用它,这样可以减少编写代码的数量,减少出错的概率。因为一旦忘记在finally里unlock,代码可能会出很大的问题,而使用synchronized更安全
  3. 如果特别需要Lock的特殊功能,比如尝试获取锁、可中断、超时功能等才使用Lock

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com