多线程的特性

Java 并发系列(一)多线程三大特性:原子性、可见性、有序性

 

1. 原子性

原子性是指:多个操作作为一个整体,不能被分割与中断,也不能被其他线程干扰。如果被中断与干扰,则会出现数据异常、逻辑异常。

多个操作合并的整体,我们称之为复合操作。一个复合操作,往往存在前后依赖关系,后一个操作依赖上一个操作的结果。如果上一个操作结果被其他线程干扰,对于当前线程看来整个复合操作的结果便不符合预期。同理线程也不能在复合操作中间被中断,中断必须发生在进入复合操作之前或者等到复合操作结束之后。

保证原子性就是在多线程环境下,保证单个线程执行复合操作符合预期逻辑。

典型的复合操作:『先检查后执行』和『读取—修改—写入』

如何保证线程的一致性

使用较多的三种方式:

内置锁(同步关键字):synchronized;

显示锁:Lock;

自旋锁:CAS;

当然这三种实现方式和保证同步的机制上都有所不同,在这里我们不做深入的说明。

加锁 synchronized ,什么是锁,锁实现的就是将你内部的操作保证你操作的时候没有别人修改过。

乐观锁
乐观锁也称为自旋锁也就是cas,这种机制大致就行,我修改一个值,比如a=3,我需要给他+1操作 ,此时在我+1操作的时候会判断a是不是还是等于3,如果等于三则进行+1操作 cas内部机制是原子的,不会出现判断了之后被别的线程修改了,我才又执行加一的操作

ABA情况
上面说了乐观锁会又判断操作然后修改。

假设 我预计的值为 1,有一个线程把他改为2了又改为1了那我是不是就不知道了,此时就有了版本号的概念。则每次操作这个值版本号会更新。

此时又会有问题,这样的话不影响最终的结果啊
但是如果是对象类型呢 假设你需要对象a内有属性被更改了,或者逻辑被更改了,但是引用指向的对象并没有改此时你将无法感知

悲观锁
悲观锁之前先大致说一下用户态于内核态

笼统来说 用户态无法执行类似操作系统的东西,当需要用到时候需要申请,需要耗费大量的资源
我的代码程序就是用户态

悲观锁的概念呢就是,我开始以为你会被别人修改,我来的时候就把你锁定住,别的线程来了,就先等待挂起,我执行完了你再进行抢夺这个锁,抢到了再执行

锁定这个操作需申请所以开销很大

乐观锁效率与悲观锁效率
乐观锁:
优点 不需要申请资源,所以一开始开销比较小
缺点 以为你的线程当不满足条件的时候会一直自旋占用资源所以当,并发量高的时候则不建议用乐观锁

悲观锁:
优点 当线程多的时候,只有一个线程执行,其他线程是挂起的,不会消费资源所以线程多的时候效率高于乐观锁

缺点 一开始开销大

synchronized 的锁升级机制
偏向锁 ----> 乐观锁(轻量级锁) ----> 悲观锁(重量级锁)

当有竞争的时候 由偏向锁升级为轻量级
当竞争到达设定的值得时候 轻量级 升级为重量级锁

中间还有匿名偏向锁的过程,了解即可
当并发高的时候,可以关闭偏向锁,或者直接升级为重量级锁,详情查一下api即可,给予jdk8来说 之前synchronized 是直接重量级锁

2. 可见性

可见性问题是指,一个线程修改的共享变量,其他线程是否能够立刻看到。对于串行程序而言,并不存在可见性问题,前一个操作修改的变量,后一个操作一定能读取到最新值。但在多线程环境下如果没有正确的同步则不一定。

如何保持可见性

如何保持数据可见性
此时就有一个关键字 Volatile
知道了问题大致就行猜到 Volatile 的作用了,就是将我们修改的值,立刻同步内存并同步到所有引用中去

3. 有序性

有序性问题是指从观察到的结果推测,代码执行的顺序与代码组织的顺序不一致。

使用synchronized、volatile,加锁lock等方式一般及可以保证线程的可见性与有序性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值