网易云课堂学习之 -- JAVA多线程

一、有这样一道面试题

面试题目:实现一个 限流场景中 使用的计数器,说出你的方案

二、实现方案

第一种实现方案:直接使用int变量 i++ 操作

计数器最终结果: 17477。<=2000,说明存在多线程安全问题

能用,局限于 非多线程 场景

为什么会出现 多线程下 计算不准确的问题?

java提供的命令:javap -v xx.class 可以帮我们看懂字节码文件

我们可以看到 JAVA在这里的底层操作:1、获取当前值;2、计算;3、赋值 。两个线程同时获取到同一个当前值,提交的时候第一个线程提交,第二个线程提交覆盖第一个线程提交的。

多线程 -- 线程安全 -- 原子性问题

如何保证原子性 ?

操作中多个步骤执行过程中,涉及的核心资源(数据)保持一致。

灵魂拷问:剩下的这两种方案你选哪一种?理由是什么?

第二种实现方案: 加锁(synchronized或lock )

public sychronized int incr()

计数器的结果:20000

锁在这种情况下使用存在的不足:锁在争抢的过程中会让线程进入阻塞的状态,阻塞就意味着需要操作系统下一次调度才能执行。

操作系统调度的两个因素: 1、服务器上线程的数量 --》JAVA服务器尽量不要跑其他软件,线程池控制线程数量 2、其他线程执行的任务类型。有的人回想,若CPU上有一个耗时很长的线程,一直没有CPU释放会出现什么情况?这个问题CPU早就想到了,它的每个线程执行到指定时间后,都会退出进入阻塞状态,和其他线程共同竞争。如果该线程还没执行完成,那就再次进行CPU中执行,循环一直到线程执行完成

第三种实现方案: JDK提供计数器AtomicInteger

AtomicInteger i = new AtomicInteger();

计数器的结果:20000

为什么大家都选这个?CAS ( Compare And Swap

什么是CAS机制(https://www.cnblogs.com/myopensource/p/8177074.html)
https://blog.csdn.net/qq_32998153/article/details/79529704

底层原理机制--》1、最后提交时,先对比检查数据有没有变化 2、如果一致则操作成功。否则重头再来

优点:锁还有CPU重新调度的时间问题,CAS不存在,只是在当前CPU下循环调用

三、分析以上三种方案--方案一

3.1 代码实现

3.2 为什么会出现多线程安全问题

这需要看JAVA底层是怎么操作的。如下图:CPU-0和CPU-1操作后理论上应该是2,但是实际结果有可能是1。
这是因为CPU-0和CPU-1都从内存中读到的i=0,i++后都得到i=1,两次赋值都是1。这就是多线程下面的线程安全问题(原子性没有得到保证)。

3.3 原子性是什么

原子性,我们在数据库事务ACID中应该听到过。数据库使用锁机制来保证原子性。

如何保证原子性 ?

操作中多个步骤执行过程中,涉及的核心资源(数据)保持一致。

四、分析以上三种方案--方案二

4.1 代码实现

4.2 原理图

4.3 线程的状态

NEW:尚未启动的线程的线程状态。

RUNNABLE:可运行线程的线程状态。
可运行线程状态正在Java虚拟机中执行,但它可能正在等待操作系统中的其他资源例如处理器。

BLOCKED:等待监视器定时阻塞的线程的线程状态

WAITING:等待线程的线程状态。

TIMED_WAITING:具有指定等待时间的等待线程的线程状态。

TERMINATED(terminated):终止线程的线程状态。线程已完成执行。

=========Java源码里提供了6种线程的状态。但我们在网上搜索线程状态,有五种有七种的,是为什么呢?这是和底层操作系统有关的。为了统一,Java代码实现了6种状态,应用在JVM上,至于JVM和底层操作系统的交互,直接被封装起来了。==========

五、分析以上三种方案--方案三

5.1 代码实现

5.2 原理


5.3 什么是CAS机制

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值