JAVAEE初阶相关内容第十一弹--多线程(进阶)

这篇具有很好参考价值的文章主要介绍了JAVAEE初阶相关内容第十一弹--多线程(进阶)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、常见的锁策略

1乐观锁VS悲观锁

1.1乐观锁

1.2悲观锁

2.轻量级锁VS重量级锁

2.1轻量级锁

2.2重量级锁

3.自旋锁VS挂起等待锁

3.1自旋锁

3.2挂起等待锁

4.互斥锁VS读写锁

4.1互斥锁

4.2读写锁

5.公平锁VS非公平锁

5.1公平锁

5.2非公平锁

6.可重入锁VS不可重入锁

6.1可重入锁

6.2不可重入锁

7.关于synchronized

二、CAS

1.CAS涉及的下操作:

2.CAS的应用场景

2.1实现原子类

伪代码​编辑

2.2实现自旋锁

伪代码

3.CAS中的ABA问题

三、Synchronized原理

1.锁升级/锁膨胀

1.1无锁

1.2偏向锁

1.3轻量级锁

1.4重量级锁

2.锁消除

3.锁粗化


一、常见的锁策略

接下来进行学习的内容不仅仅局限于java,任何和“锁”相关的话题,都会涉及到。

1乐观锁VS悲观锁

站在锁冲突概率的预测角度

1.1乐观锁

假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误信息,让用户决定如何去做。

1.2悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

2.轻量级锁VS重量级锁

2.1轻量级锁

加锁解锁开销较小,效率更高

2.2重量级锁

开锁解锁开销较大,效率更低

站在加锁操作的开销角度

3.自旋锁VS挂起等待锁

3.1自旋锁

典型的轻量级锁,更大几率获取到锁,加锁速度快

3.2挂起等待锁

典型的重量级锁,“傻等”,被动的等待,省下来CPU去做别的工作,加锁的时间比较长。

4.互斥锁VS读写锁

4.1互斥锁

互斥锁:就是前面用过的像synchronized这样的锁,提供加锁和解锁两个操作。如果一个线程加锁了,另一个线程也尝试加锁,就会阻塞等待。

4.2读写锁

提供三种操作:(1)针对读加锁。(2)针对写加锁。(3)解锁

基于一个事实:多线程针对同一个变量并发读,这个时候是没有线程安全问题的,也不需要加锁控制。(读写锁就是针对这种情况采取的特殊的处理)

读锁和读锁之间没有互斥。写锁和写锁之间存在互斥。写锁和读锁之间存在互斥。(当前代码中,如果只是读操作,加读锁即可,如果有写操作,加写锁。)

5.公平锁VS非公平锁

5.1公平锁

这里的公平定义为:先来后到。B比C先来的,当A释放锁后,B就能先于C获取到锁。

5.2非公平锁

不遵守先来后到,B和C都有可能获取到锁。

OS内部的线程调度就可视为是随机的,如果不做任何额外的限制,锁就是非公平的,如果要想实现公平锁,就需要额外的数据结构,来记录线程们的先后顺序。

公平锁和非公平锁之间没有好坏之分,关键还得看适用场景。

6.可重入锁VS不可重入锁

6.1可重入锁

一个线程针对一把锁,连续加锁多次不会死锁。

6.2不可重入锁

一个线程针对一把锁,连续加锁两次,出现死锁。

7.关于synchronized

(1)synchronized既是一个悲观锁,也是一个乐观锁

synchronized默认是乐观锁,但是如果发现当前锁竞争比较激烈,就会变成悲观锁。

(2)synchronized既是轻量级锁,也是重量级锁。

synchronized默认是轻量级锁,如果发现当前锁竞争比较激烈的话,就会变成重量级锁。

(3)synchronizaed这里的轻量级锁,是基于自旋锁的方式实现的。synchronized这里的重量级锁是基于挂起等待锁的方式实现的。

(4)synchronized不是读写锁。

(5)synchronized是非公平锁。

(6)synchrnized是可重入锁。

上述谈到的六种锁策略可以视为是“锁的形容词”

二、CAS

全称:Compare and swap  比较和交换

1.CAS涉及的下操作:

我们设内存中的原始数据V,旧的预期值A,需要修改的新值B

1.比较A与的V值是否相等(比较)

2.如果比较相等。将B写入V(交换)

3.返回操作是否成功

此处特别的是,上述的CAS的过程并不是通过一段代码实现的,而是通过一条CPU指令完成的。也就是说CAS操作是原子的,就可以在一定程度上回避线程安全问题,所以说我们解决线程安全问题除了加锁之外就又有了一个新的方向。

CAS可以理解为是CPU给咱们提供的一个特殊的指令,通过这个指令,就可在一定程度上处理线程安全问题。

2.CAS的应用场景

2.1实现原子类

JAVA标准库中提供的类

AtomicInteger count = new AtomicInteger(0);
伪代码JAVAEE初阶相关内容第十一弹--多线程(进阶),Javaee,java-ee,java

使用原子类来解决线程安全问题代码实现:

创建两个线程,t1和t2,在前面的学习中,当两个线程不加锁的时候就会出现bug,所以采用了加锁策略,这里使用原子类来实现不需要加锁也可以达到预期的效果:

public class ThreadD28 {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger count = new AtomicInteger(0);
        //使用原子类来解决线程安全问题
        Thread t1 = new Thread(() ->{
            for (int i = 0; i < 50000; i++) {
                //因为java不支持运算符重载,所以只能使用普通方法来表示自增自减
                count.getAndIncrement();
            }
        });
        Thread t2 = new Thread(() ->{
            for (int i = 0; i < 50000; i++) {
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count.get());
    }
}

运行结果:

JAVAEE初阶相关内容第十一弹--多线程(进阶),Javaee,java-ee,java

2.2实现自旋锁

伪代码

JAVAEE初阶相关内容第十一弹--多线程(进阶),Javaee,java-ee,java

3.CAS中的ABA问题

CAS在运行中的核心,检查value和oldValue是否一致,如果一致就视为value没有被修改过,所以进行下一步的交换操作是没问题的。但是需要注意的是,这里的一致,可能是改过但是还原回来的。【买手机,可能是新机也有可能是翻新机,被销售商回收了,经过一些翻新的操作,将外壳换掉,重新包装】

下面看一个取钱的例子(概率极低!!)

JAVAEE初阶相关内容第十一弹--多线程(进阶),Javaee,java-ee,java

以上情况发生的概率极低,但是这种问题一旦出现的话就是容易解决的,提前防患于未然是更好的选择。针对当前的问题,采取的方案就是加上一个版本号想象成初始的版本号是1,每次修改的版本号都+1,然后进行CAS的时候,就不是以金额为准了,而是以版本号为基准,此时版本号要是没变就一定没发生改变(版本号只能增长,不能降低)

三、Synchronized原理

两个线程针对同一个对象加锁,就会产生阻塞等待。

synchronized内部还有一些优化机制,存在的目的是了让这个锁更高效更好用。

1.锁升级/锁膨胀

1.1无锁

1.2偏向锁

在进行加锁的时候,首先要进入到偏向锁的状态。偏向锁并不是真正的加锁,而是占个位置,有需要才会进行加锁,没需要就不必加。相当于“懒汉模式”中提到的懒加载一样。偏向锁的状态,做个标记(这个过程是非常轻量的)如果使用锁的过程中,没有出现锁竞争在synchronized执行完之后,解除偏向锁即可,但是如果使用过程中,另一个线程也尝试加锁,这个时候就会迅速的将偏向锁升级称为真正的加锁状态,另外的一个线程也只能阻塞等待了。

1.3轻量级锁

当synchronized发生锁竞争的时候,就会从偏向锁升级为轻量级锁,此时,synchronized相当于通过自旋的方式来进行加锁的(就类似于上述的CAS中的伪代码)

1.4重量级锁

如果要是很快别人就释放了锁,自旋还是划算的,但是如果迟迟拿不到锁,一直自旋是不划算的,synchronized自旋不是无休止的,自旋到一定程度,就会在再次升级成为重量级锁(挂起等待锁)。这个锁则是基于操作系统的原生API来进行加锁的,linux原生提供了mutex一组API,操作系统内核提供的加锁功能,这个锁会影响到线程的调度。此时如果线程进行了重量级的加锁,并且发生了锁竞争,此时线程会被放在阻塞队列中,不参与CPU的调度。然后直到锁被释放,这个线程才有机会被调度到,并且有机会获取到锁。

2.锁消除

编译器智能的判定,看当前代码是否真的要加锁,如果这个场景不需要加锁,程序员加了,就会自动的把锁消除。

例如StringBuffer,关键的方法有synchronized,但是如果在单线程中使用StringBuffer,synchronized加了也白加,此时编译器就会直接将加锁操作消除。

3.锁粗化

锁的粒度:synchronized包含的代码越多,粒度就越粗,包含的代码越少,粒度就越细。

一般情况下,认为锁的粒度细一点是比较好的,加锁部分的代码是不能并发执行的,锁的粒度越细,能并发的代码就越多,反之则越少。但是有些情况下,锁的粒度粗一些就更好。

十一弹的续集会进行更新这一部分中在面试中的高频考点~文章来源地址https://www.toymoban.com/news/detail-733262.html

到了这里,关于JAVAEE初阶相关内容第十一弹--多线程(进阶)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++第十一弹---类与对象(八)

       ✨ 个人主页:  熬夜学编程的小林 💗 系列专栏:   【C语言详解】   【数据结构详解】 【C++详解】 目录 1、友元 1.1、友元函数 1.2、友元类 2、内部类 3、匿名对象 4、拷贝对象时的一些编译器优化 总结 友元提供了一种突破封装的方式,有时提供了便利。 但是友元会增

    2024年03月26日
    浏览(45)
  • 每天40min,我们一起用70天稳扎稳打学完《JavaEE初阶》——12/70 第十二天【线程池 面试题】

    专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录) 文章字体风格: 红色文字表示:重难点★✔ 蓝色文字表示:思路以及想法★✔ 如果大家觉得有帮助的话,感谢大家帮

    2024年02月13日
    浏览(51)
  • javaee初阶———多线程(三)

    T04BF 👋专栏: 算法|JAVA|MySQL|C语言 🫵 小比特 大梦想 此篇文章与大家分享多线程专题第三篇,关于 线程安全 方面的内容 如果有不足的或者错误的请您指出! 我们在前面说过,线程之间是抢占式执行的,这样产生的随机性,使得程序的执行顺序变得不一致,就会使得程序产生不同的结

    2024年04月16日
    浏览(38)
  • JavaEE初阶:多线程 - 编程

    我们在之前认识了什么是多进程,今天我们来了解线程。 一个线程就是一个 \\\"执行流\\\". 每个线程之间都可以按照顺讯执行自己的代码. 多个线程之间 \\\"同时\\\" 执行 着多份代码. 引入 进程 这个概念,主要是为了解决并发编程这样的问题。因为cpu进入了多核心的时代,要想进一步

    2024年02月12日
    浏览(37)
  • 【JavaEE初阶】 线程安全

    线程安全是多线程编程是的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且准确的执行,不会出现数据污染等意外情况。上述是百度百科给出的一个概念解释。换言之,线程安全就是某

    2024年02月08日
    浏览(53)
  • 多线程(JavaEE初阶系列7)

    目录 前言: 1.常见的锁策略 1.1乐观锁和悲观锁 1.2轻量级锁和重量级锁 1.3自旋锁和挂起等待锁 1.4互斥锁与读写锁 1.5可重入锁与不可重入锁 1.6公平锁与非公平锁 2.CAS 2.1什么是CAS 2.2自旋锁的实现 2.3原子类 3.synchronized 3.1synchronized的原理以及基本特点 3.2偏向锁 3.3轻量级锁 3.4重

    2024年02月14日
    浏览(36)
  • JavaEE初阶:Java线程的状态

    目录 获取当前线程引用 休眠当前线程  线程的状态 1.NEW               2.TERMINATED  3.RUNNABLE 4.WAITING 5.TIMED_WAITING 6.BLOCKED 多线程的意义 单线程  多线程 这个方法返回当前线程的引用。但是我们会对static有疑惑,这其实是一个静态方法,更好的说法是这是一个 类方法, 调用这

    2024年02月11日
    浏览(42)
  • 多线程(JavaEE初阶系列4)

    目录 前言: 1.单例模式 1.1饿汉模式 1.2懒汉模式 1.3结合线程安全下的单例模式 1.4单例模式总结 2.阻塞式队列 2.1什么是阻塞队列 2.2生产者消费者模型 2.2.1 上下游模块之间进行“解耦合” 2.2.2削峰填谷 2.3阻塞队列的实现 结束语: 在上节中小编主要与大家分享了多线程中遇到

    2024年02月15日
    浏览(53)
  • 多线程(JavaEE初阶系列2)

    目录 前言: 1.什么是线程 2.为什么要有线程 3.进程与线程的区别与联系 4.Java的线程和操作系统线程的关系 5.多线程编程示例 6.创建线程 6.1继承Thread类  6.2实现Runnable接口 6.3继承Thread,使用匿名内部类 6.4实现Runnable接口,使用匿名内部类 6.5lambda表达式创建Runnable子类对象 7.

    2024年02月15日
    浏览(39)
  • 【JavaEE初阶】 线程安全的集合类

    原来的集合类, 大部分都不是线程安全的. Vector, Stack, HashTable, 是线程安全的(不建议用), 其他的集合类不是线程安全的. 为什么不建议使用呢? 因为我们在使用的时候,这些类就会自动的加锁,虽然编译器会自动优化为没有锁竞争的线程进行锁消除的优化,但是呢万一编译器没

    2024年02月08日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包