并发编程(三)原子性(2)

【 悲观锁与乐观锁 】:

【悲观锁】:

一定会被别人打断;我必须得上锁。synchronized就是悲观锁。

【乐观锁】:

乐观锁又称之为——无锁、自旋锁、CAS 。
厕所里的人认为不会有其他人来上厕所和我竞争。

【举例解释CAS操作】:

在这里插入图片描述
//还是拿多个线程访问 n++ 为例。
在这里插入图片描述
【往回写的时候要进行判断】:
判断是否依然为0(和拿之前的值一样),如果是,就说明中间没有线程来动它。
【如果发现不想等】:
判断的时候如果发现已经被改成8了,这个时候就得重新读 , 重新把8读过来,进行++操作,然后再去判断看一看是否依然是8,如此一直循环,直到有一次发现——没有其他线程修改,那么就成功了。

【CAS中的ABA问题】:

此0非彼0问题

其他线程修改数次最后值和原值相同。

//有些情况下,我们必须要解决ABA问题。——拿的是引用类型的话,引用指向的内容变化了。
【解决方法】:
加上一个version即可。规定任意的操作都会让这个version的值加加。

【版本的类型】:

(1)带时间戳 / 带数字
(2)布尔类型

【CAS的底层原子性保障】:

compare and swap
CAS操作本身必须得是原子性的才可以。
比如:
if( 变量==原先 ){
修改变量的值; //进行到这一步的时候,有一个线程把值改成了8,但已经过了判断,此时就只能把8改成1了。
}

【 Atomic类深入认识CAS 】:

/**
 * 解决同样的问题的更高效的方法,使用AtomXXX类
 * AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的
 */
package T05_YuanZiXing;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class T00_03_AtomicInteger {
	/*volatile*/ //int count1 = 0;

	AtomicInteger count = new AtomicInteger(0);

	/* synchronized */void m() {
		for (int i = 0; i < 10000; i++)
			//if count1.get() < 1000
			count.incrementAndGet(); //count1++
	}

	public static void main(String[] args) {
		T00_03_AtomicInteger t = new T00_03_AtomicInteger();

		List<Thread> threads = new ArrayList<Thread>();

		for (int i = 0; i < 100; i++) {
			threads.add(new Thread(t::m, "thread-" + i));
		}

		threads.forEach((o) -> o.start());

		threads.forEach((o) -> {
			try {
				o.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});

		System.out.println(t.count);

	}
}

【最终输出】:
在这里插入图片描述

【Hotspot理解CAS】:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
atomic这个类上的Linux实现 , X86架构下的 。

在这里插入图片描述
在这里插入图片描述
【94行】:
首先判断是不是MP , Multi-Processors , 你是不是多核的处理器 ,如果你是MP的话,前面加一条LOCK指令 ;
在这里插入图片描述
后面的汇编指令是cmpxchgl , CPU在底层的时候支持汇编指令cmpxchgl 。

最终实现:

cmpxchg = cas修改变量值

lock cmpxchg 指令

硬件:

lock指令在执行的时候视情况采用缓存锁或者总线锁。

【最终总结】:
如果你是多核的——加lock ;
不是多核的——直接cmpxchg指令即可。

【原子与否】:
cmpxchg指令这个不是原子的。———《汇编语言操作手册》

【得出的结论】:

CAS操作得益于CPU在汇编指令操作上支持cmpxchg指令。多核的话要在cmpxchg指令前加Lock指令。CAS底层还是使用了悲观锁,虽然叫“乐观锁”。

【 乐观锁 和 悲观锁 的效率比较 】:

【按使用场景比较】:

不同的场景:

临界区执行时间比较长 , 等的人很多 -> 重量级

时间短,等的人少 -> 自旋锁

【实现】:

和悲观锁关联的往往有一个队列;线程在队列里等着。——在队列里等着的那些线程是不消耗CPU资源的。
在这里插入图片描述
乐观锁是一圈一圈的自旋:
在这里插入图片描述
//一圈一圈的循环( 和原值进行比较 ),是需要消耗CPU资源的。

【CPU资源】:

乐观锁消耗的CPU资源更多一些。

【如何选择】:

执行时间长 , 排队的人又多————悲观锁。
执行时间短, 排队等待的人少—-——乐观锁。

【追求量化的话】:

自己去压力测试。

【实战选择】:

实战就用synchronized即可~

synchronized做了一系列的优化 , 在它内部既有自旋锁 ,偏向锁 , 重量级锁进行自适应的升级过程,它的效率已经升级的很不错了,自动完成锁升级过程。

【synchronized和三大特性 】:

【synchronized保障可见性】:

在这里插入图片描述
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值