概念:CAS是基于乐观锁思想的一种实现机制,主要是用来在不加锁的情况下,保证线程安 全的.
首先:先要了解一下什么是锁;
当多个线程同时对贡献资源进行操作时,会有线程安全的问题.可以通过加锁的方式来解决线程安全的问题
加锁方式1:sychronized
Thread threadA = new Thread(() -> { synchronized (obj) { for (int i = 0; i < 100; i++) { System.out.println(i);//输出正数 //notify() 唤醒正在当前锁对象等待的单个线程,如果有多个线程,则选择其中一个线程进行唤醒 obj.notify();//唤醒等待的线程B try { obj.wait();//等待B输出完毕 } catch (InterruptedException e) { e.printStackTrace(); } } obj.notify();//唤醒可能还在等待的线程B } });
加锁方式2:lock锁
try { lock.lock(); String name = Thread.currentThread().getName(); if(this.money>=money){ System.out.println(name+"来取钱"+money+"成功"); this.money-=money; System.out.println(name+"来取钱后,余额剩余: "+this.money); }else { System.out.println(name+"取钱失败!余额不足."); } } finally { lock.unlock(); }
乐观锁的思想:不加锁实现线程安全
CAS基于这个思想,对八大基本数据类型进行了一次包装:八大原子类,直接对原件进行操作,在多个线程同时对原子类变量进行操作时,每个线程先拿到一份变量的值存到自己线程的小内存中.,这里有一个细节,这个变量被关键字Volatile修饰,保障了线程的可见性.
每个线程在对变量操作时会先保存一份老值,在同步变量的新值时,先对比老值是否和原来一致,如果不一致就不对其进行同步.再次获取老值,对变量进行操作,底层就是一个while循环,直到满足老值与新值一致,跳出while循环.
好处:
极大的提高了效率,比加锁提高了三倍不至!它节省的就是cup沉睡和唤醒线程的效率.
坏处:
局限性极大,只有在对单个变量操作时适用.如果是很长的代码块.如果一直有一个线程占用共享变量
,那么其他线程将一直在死循环里,长时间的话会极大的降低效率.
CAS的底层并非Java代码实现的,CAS的底层是调用的C++的方法,而C++又是调用的汇编语言的函数,在这个汇编函数里有两条对原子操作的汇编指令其实加了lock锁.
CAS引发的aba问题:
假如有线程A,线程B两个线程.这时有一个变量C.
线程AB同时对C进行+1操作.
在线程A对变量C+1之后,这时如果cpu分配的时间片到了,线程B拿到锁进入对C进行+1,再-1.变量已经发生了一次变化, 线程A醒来,不会发现变量已经发生过一次改变,仍正常对其进行操作,有可能会引发不可名状的问题.这就是ABA问题.
给变量加上一个版本号即可解决这个问题.