• 并发编程(三)原子性(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);
    
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

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

    【Hotspot理解CAS】:

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

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

    最终实现:

    cmpxchg = cas修改变量值

    lock cmpxchg 指令
    
    • 1

    硬件:

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

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

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

    【得出的结论】:

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

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

    【按使用场景比较】:

    不同的场景:

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

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

    【实现】:

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

    【CPU资源】:

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

    【如何选择】:

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

    【追求量化的话】:

    自己去压力测试。

    【实战选择】:

    实战就用synchronized即可~

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

    【synchronized和三大特性 】:

    【synchronized保障可见性】:

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

  • 相关阅读:
    国科云SSL证书讲堂:SSL证书是否支持多个域名?
    MindSpore:强化学习基础-蒙特卡洛(Monte Carlo)
    MySQL(5)事务
    Go :复制预声明功能的半穷举测试(附完整源码)
    暴雪网易事件大讨论:Web3游戏未来发展趋势
    springcloud之nacos服务治理
    xxe攻击(XML外部实体)
    【模板引擎】freemarker模板引擎的常用命令介绍
    Centos (含Rocky-Linux) VSFTPD 简单设置
    每日一题 —— LC. 775 全局倒置与局部倒置
  • 原文地址:https://blog.csdn.net/fuyuanduan/article/details/127993662