目录
1.自旋
2、CAS
3、自旋锁应用示例 仿 Lock
一、自旋
有三个线程,我想要第三个线程始终满足最后一个执行,那么我可以采取只要前两个线程没有执行完毕,我就在第三个线程前面始终来一个自旋等待,只要线程的数量自旋到只剩一个的时候,就让第三个线程执行
//自旋等待例子
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+ " 开始执行");
try {Thread.sleep(3000);}catch(Exception e) {}
System.out.println(Thread.currentThread().getName()+ " 执行结束");
}
}).start();
new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName()+ " 开始执行");
try {Thread.sleep(3000);}catch(Exception e) {}
System.out.println(Thread.currentThread().getName()+ " 执行结束");
}
}).start();
while(Thread.activeCount()!=1) {
//自旋等待
}
System.out.println("全部执行完毕");
}
二、CAS
1)悲观锁和乐观锁
== 悲观锁 ==
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都
会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,
其它线程阻塞,用完后再把资源转让给其它线程)。
传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都
是在做操作之前先上锁。
== 乐观锁 ==
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在
更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和
CAS 算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,
像数据库提供的类似于write_condition机制,其实都是提供的乐观锁
2)Synchronized的问题
synchronized保证了线程的安全,但在某些情况下,不是一个最优的选择
synchronized 关键字会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,
这个过程中涉及到操作系统用户模式和内核模式的转换,代价比较高。尽管Java1.6 为Synchronized做了优化,
增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低
3) 什么是CAS
CAS是英文单词 Compare And Swap 的缩写,翻译过来就是比较并替换。
CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
用unsafe提供了原子性操作方法。
从思想上来说,Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。
CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。
CAS的缺点:
1.CPU开销较大
在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很大的压力。
2.不能保证代码块的原子性
CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。
3.ABA问题
当一个值从A更新成B,又更新会A,普通CAS机制会误判通过检测。
利用版本号比较可以有效解决ABA问题
从Java1.5 开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。
这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,
如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
三、自旋锁应用示例 仿 Lock
public class MyLock {
AtomicReference<Thread> atomicReference =new AtomicReference<>();
public void lock() {
Thread thread=Thread.currentThread();
while(!atomicReference.compareAndSet(null, thread)) {
}
}
public void unlock() {
Thread thread=Thread.currentThread();
while(! atomicReference.compareAndSet(thread, null)) {
}
}
}
public class Test3 {
public static void main(String[] args) {
SaleThread th=new SaleThread();
new Thread(th).start();
new Thread(th).start();
new Thread(th).start();
new Thread(th).start();
}
}
class SaleThread implements Runnable{
int ticket=100;
MyLock lock=new MyLock();
public void run() {
while(true) {
try {
lock.lock();
if(ticket>0) {
try {Thread.sleep(10);}catch(Exception ex) {}
System.out.println(Thread.currentThread().getName()+" 卖到第 " + ticket-- +" 张票 ");
}
else {
break;
}
}
catch(Exception ex) {
ex.printStackTrace();
}
finally {
lock.unlock();
}
}
}
}