18.多线程中的锁有哪一些?

锁的分类

根据分类标准我们把锁分为以下 7 大类别,分别是:

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

  • 可重入锁/非可重入锁;

  • 共享锁/独占锁;

  • 公平锁/非公平锁;

  • 悲观锁/乐观锁;

  • 自旋锁/非自旋锁;

  • 可中断锁/不可中断锁。

以上基本是我们经常会遇到的一些锁。

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

第一种分类是偏向锁/轻量级锁/重量级锁,这三种锁特指 synchronized 锁的状态,通过在对象头中的 mark word 来表明锁的状态。

偏向锁

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

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

轻量级锁

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

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

重量级锁

重量级锁是互斥锁,它是利用操作系统的同步机制实现的,所以开销相对比较大。

当多个线程直接有实际竞争,且锁竞争时间长的时候,轻量级锁不能满足需求,锁就会膨胀为重量级锁。

重量级锁会让其他申请却拿不到锁的线程进入阻塞状态。

你可以发现锁升级的路径:无锁→偏向锁→轻量级锁→重量级锁。

锁升级的过程是JVM自动完成的,开发者无需手动干预,在实际开发中合理的使用锁,可以避免不必要的升级过程,从而提高应用程序的性能和可伸缩性。

  • 偏向锁性能最好,可以避免执行 CAS 操作。
  • 而轻量级锁利用自旋和 CAS 避免了重量级锁带来的线程阻塞和唤醒,性能中等。
  • 重量级锁则会把获取不到锁的线程阻塞,性能最差。

2、可重入锁/非可重入锁

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

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

对于可重入锁而言,最典型的就是 ReentrantLock 了,正如它的名字一样,reentrant 的意思就是可重入,它也是 Lock 接口最主要的一个实现类。

3、共享锁/独占锁

  • 共享锁指的是我们同一把锁可以被多个线程同时获得、
  • 而独占锁指的就是,这把锁只能同时被一个线程获得。

我们的读写锁,就最好地诠释了共享锁和独占锁的理念。

  • 读写锁中的读锁,是共享锁,
  • 而写锁是独占锁。

读锁可以被同时读,可以同时被多个线程持有,而写锁最多只能同时被一个线程持有。

4、公平锁/非公平锁

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

而非公平锁就不那么“完美”了,它会在一定情况下,忽略掉已经在排队的线程,发生插队现象。

5、悲观锁/乐观锁

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

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

6、自旋锁/非自旋锁

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

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

7、可中断锁/不可中断锁

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

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

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
2. Excel协议族包括COM协议和OLE协议。其,函数原型如下: COM协议:HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv); OLE协议:HRESULT OleCreate(CLSID rclsid, REFIID riid, DWORD renderopt, LPFORMATETC lpFormatEtc, LPOLECLIENTSITE pClientSite, LPSTORAGE pStg, LPVOID* ppvObj); 3. 创建线程的函数是pthread_create,创建进程的函数是fork。 4. 线程间临界资源可以通过互斥或信号量来保护。条件变量可以用来协调线程之间的通信和同步。 5. 编译有线程的文件需要加-lpthread参数。 6. 线程是进程内的一条执行路径,它与进程共享内存和系统资源,但拥有独立的栈空间和程序计数器。进程是系统正在执行的一个程序实例,它拥有独立的内存空间和系统资源。 7. 多进程适用于需要独立运行的任务,多线程适用于需要共享内存和系统资源的任务。 8. 线程是进程内的一条执行路径,它可以共享进程的内存和系统资源,因此在多任务处理、共享数据等方面具有优势。而多进程需要独立的内存空间和系统资源,因此相对来说比较耗费资源。 9. 创建信号量的函数原型为:sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); 10. 互斥的实现方法可以使用互斥,通过对共享资源的加和解来保护临界资源。 11. 二进制信号量只有两个状态,1和0,用于实现互斥;而互斥信号量可以有多个状态,用于控制多个进程/线程之间的同步。 12. 产生死的必要条件是:互斥、占有和等待、不可剥夺、循环等待。解决死的方法包括资源有序分配法、银行家算法、死检测与恢复等。 13. 可重入是指一个函数在多个任务之间可以安全地共享,不会造成数据混乱或破坏性的影响。 14. 可以使用互斥或条件变量来实现一个线程阻塞另一个线程。 15. HTTP 403表示服务器拒绝请求,HTTP 302表示请求的资源已经临时转移。 16. 子网掩码为255.255.255.245可以表示15个IP地址。 17. 192.162.1.1是C类网络ip地址。 18. ISO模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。集线器是物理层设备,交换机是数据链路层和网络层设备,路由器是网络层设备。它们的工作原理分别是将信号转发、根据MAC地址转发和根据IP地址转发。 19. IP通过DNS协议转成域名和通过ARP协议转成MAC地址。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞四海

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值