CAS的全称为Compare And Swap,直译就是比较交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,在intel的CPU中,使用的是cmpxchg指令,就是说CAS是靠硬件实现的,从而在硬件层面提升效率。
CAS (compareAndSwap),中文叫比较交换,一种无锁原子算法。过程是这样:它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。CAS 操作时抱着乐观的态度进行的,它总是认为自己可以成功完成操作。
AQS(abstractQueueSychronizer)同步发生器。通过内置得到FIFO同步队列来完成线程争夺资源的管理工作。
1 /** 2 * AtomicInteger的用法 3 * @author Administrator 4 * 5 */ 6 public class CAS2 { 7 //声明原子型变量 8 private static AtomicInteger atomic1 = new AtomicInteger(100); 9 10 public static void main(String[] args) throws Exception {11 /**12 * compareAndSet(期望值,改变值)13 */14 Thread t1 = new Thread(()->{15 System.out.println("t1:" atomic1.compareAndSet(100, 110));16 System.out.println("t1.atomic1:" atomic1.get());17 });18 t1.start();19 20 Thread t2 = new Thread(()->{21 try {22 TimeUnit.SECONDS.sleep(2);23 } catch (InterruptedException e) {24 e.printStackTrace();25 }26 System.out.println("t2:" atomic1.compareAndSet(110, 100));27 System.out.println("t2.atomic1:" atomic1.get());28 });29 t2.start();30 31 Thread t3 = new Thread(()->{32 try {33 TimeUnit.SECONDS.sleep(3);34 } catch (InterruptedException e) {35 e.printStackTrace();36 }37 System.out.println("t3:" atomic1.compareAndSet(100, 120));38 System.out.println("t3.atomic1:" atomic1.get());39 });40 t3.start();41 }42 }
1 /** 2 * AtomicStampedReference的用法 3 * @author Administrator 4 * 5 */ 6 public class CAS3 { 7 8 private static AtomicStampedReference<Integer> asf = new AtomicStampedReference<Integer>(100, 1); 9 10 public static void main(String[] args) throws Exception {11 12 /**13 * compareAndSet(期望值,改变值,当前版本号,改变后的版本号)14 */15 Thread t1 = new Thread(()->{16 System.out.println("t1:" asf.compareAndSet(100, 110, asf.getStamp(), asf.getStamp() 1));17 System.out.println("t1.asf:" asf.getReference());18 });19 t1.start();20 21 Thread t2 = new Thread(()->{22 try {23 TimeUnit.SECONDS.sleep(2);24 } catch (InterruptedException e) {25 e.printStackTrace();26 }27 System.out.println("t2:" asf.compareAndSet(110,100,asf.getStamp(),asf.getStamp() 1));28 System.out.println("t2.asf:" asf.getReference());29 });30 t2.start();31 32 Thread t3 = new Thread(()->{33 try {34 TimeUnit.SECONDS.sleep(3);35 } catch (InterruptedException e) {36 e.printStackTrace();37 }38 System.out.println("t3:" asf.compareAndSet(100, 120,asf.getStamp(),asf.getStamp() 1));39 System.out.println("t3.asf:" asf.getReference());40 });41 t3.start();42 }43 44 }
1 /** 2 * 模拟实现锁的方法 3 * @author Administrator 4 * 5 */ 6 public class MyLock implements Lock { 7 8 //实例化帮助器 9 private Helper helper = new Helper();10 /**11 * 定义内部类帮助器,方便下面方法的执行12 * @author Administrator13 *14 */15 private class Helper extends AbstractQueuedSynchronizer{16 //以独占的方式获取锁17 @Override18 protected boolean tryAcquire(int arg) {19 //调用父类的获取状态的方法20 int state = getState();21 if(state == 0){//如果状态为0 ,那说明可以获得锁22 //利用CAS原理修改state,保证原子性23 boolean stateFlag = compareAndSetState(0, arg);24 if(stateFlag){//如果未true,那么说明当前可以获得锁,进行状态修改25 //设置当前线程占有资源26 setExclusiveOwnerThread(Thread.currentThread());27 return true;//返回值,说明获取锁成功28 }29 }else if(getExclusiveOwnerThread() == Thread.currentThread()){//判断当前占有的线程是不是请求的线程30 //增加可重入性功能31 setState(getState() arg);32 return true;33 }34 //其他情况设置为false,说明获取锁失败35 return false;36 }37 38 //释放锁39 @Override40 protected boolean tryRelease(int arg) {41 //获取当前锁的状态42 int state = getState()-arg;43 //设置标志位44 boolean flag = false;45 //判断释放当前锁后,状态是否为046 if(state == 0){//说明当前线程锁全部释放47 setExclusiveOwnerThread(null);48 flag=true;49 }50 setState(state);51 return flag;52 }53 54 //条件限制,在某些条件下加锁55 public Condition newConditionObject(){56 return new ConditionObject();57 }58 }59 60 @Override61 public void lock() {62 //设置锁63 helper.acquire(1);64 65 }66 67 @Override68 public void lockInterruptibly() throws InterruptedException {69 //中断锁70 helper.acquireInterruptibly(1);71 72 }73 74 @Override75 public boolean tryLock() {76 // TODO Auto-generated method stub77 return helper.tryAcquire(1);78 }79 80 @Override81 public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {82 // TODO Auto-generated method stub83 return helper.tryAcquireNanos(1, unit.toNanos(time));84 }85 86 @Override87 public void unlock() {88 // 释放锁89 helper.release(1);90 }91 92 @Override93 public Condition newCondition() {94 // 在特定条件下给加锁95 return helper.newConditionObject();96 }97 98 }
1 /** 2 * 测试类 3 * @author Administrator 4 * 5 */ 6 public class MyLockTest { 7 8 private MyLock lock = new MyLock(); 9 private int m = 0;10 public int next(){11 //开始加锁12 lock.lock();13 try {14 return m ;15 } finally {16 //释放锁17 lock.unlock();18 }19 }20 public static void main(String[] args) {21 MyLockTest t = new MyLockTest();22 Thread[] th = new Thread[20];23 for (int i = 0; i < th.length; i ) {24 th[i] = new Thread(()->{25 System.out.println("m=" t.next());26 });27 th[i].start();28 }29 }30 }
可重入性:同一个锁当有多个同一资源进行占有的时候,直接分配给这个线程。
1 /** 2 * JDK提供的可重入性互斥锁 3 * @author Administrator 4 * 5 */ 6 public class JDKLock { 7 //使用JDK提供的可重入性锁 8 ReentrantLock lock = new ReentrantLock(); 9 public void a(){10 lock.lock();11 System.out.println("a");12 b();13 lock.unlock();14 }15 public void b(){16 lock.lock();17 System.out.println("b");18 lock.unlock();19 }20 public static void main(String[] args) {21 JDKLock t = new JDKLock();22 new Thread(()->{23 t.a();24 }).start();25 }26 27 }
构造器中的计数值(count)实际上就是闭锁需要等待的线程数量,这个值只能被设置一次,而且CountDownLatch函数没有提供任何机制去重新设置这个值。
与CountDownLatch的第一次交互是主线程等待其他线程,主线程必须在启动其他线程后立即调用CountDownLatch.await()方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务。其他N个线程必须引用闭锁对象,因为他们需要通知CountDownLatch对象,他们已经完成了各自的任务,这种通知机制是通过CountDownLatch.countDown()方法来完成的,没调用一次这个方法,在构造函数中初始化的count就会减1,所以当N个线程都用了这个方法,count的值就是0,然后主线程就能通过await()方法,恢复执行自己的任务。
1 /** 2 * 模拟航空公司卖票系统 3 * @author Administrator 4 * 5 */ 6 public class FightQueryDemo { 7 8 //公司列表 9 private static List<String> companys = Arrays.asList("东方航空","海南航空","南方航空");10 //11 private static List<String> fightList = new ArrayList<>();12 13 public static void main(String[] args) throws InterruptedException {14 //起始地15 String oringin ="BJ";16 //目的地17 String dest = "SH";18 //根据公司数量创建线程数组19 Thread[] threads = new Thread[companys.size()];20 //创建线程统计计数器21 CountDownLatch latch = new CountDownLatch(companys.size());22 23 for (int i = 0; i < threads.length; i ) {24 //获取当前公司名称25 String name = companys.get(i);26 threads[i] = new Thread(()->{27 System.out.printf("%s 查询从%s出发到%s的机票\n",name,oringin,dest);28 //随机产生票数29 int val = new Random().nextInt(10);30 try {31 TimeUnit.SECONDS.sleep(2);32 fightList.add(name "--" val);33 System.out.printf("%s 航空公司查询成功\n",name);34 //计数器减一35 latch.countDown();36 } catch (InterruptedException e) {37 e.printStackTrace();38 }39 });40 threads[i].start();41 }42 //线程执行完以后,唤醒主线程,保证执行完上面的线程,在执行下面的43 latch.await(); 44 System.out.println("=========查询结果如下============");45 fightList.forEach(System.out::println);46 }47 }
需要所有的子任务都完成时,才执行主任务,这个时候可以选择CyclicBarrier。
基本原理:每个线程执行时都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。
在CyclicBarrier的内部定义了一个Lock对象,每当一个线程调用await()方法时,将拦截的线程数加1,然后判断剩余拦截数是否为初始值parties,如果不是,进入Lock对象的条件队列等待,如果是,执行barrierAction对象的Runnable方法,然后将锁的条件队列中的所有线程放入锁等待队列中,这些线程会依次的获取锁,释放锁。
CyclicBarrier的两个构造函数CyclicBarrier(int parties)和CyclicBarrier(int parties,Runnable barrierAction),前者只需要声明需要拦截的线程数即可,而后者还需要定义一个等待所有线程到达屏障优先执行的Runnable对象。
1 /** 2 * 使用CyclicBarrier实现运动员跑道实例 3 * @author Administrator 4 * 5 */ 6 public class RaceDemo { 7 8 public static void main(String[] args) { 9 int num = 8;10 CyclicBarrier barriers = new CyclicBarrier(num);11 Thread[] player = new Thread[num];12 for (int i = 0; i < player.length; i ) {13 player[i] = new Thread(()->{14 try {15 TimeUnit.SECONDS.sleep(new Random().nextInt(5));16 System.out.println(Thread.currentThread().getName() "准备好了!");17 //设置屏障18 barriers.await();19 } catch (Exception e) {20 // TODO Auto-generated catch block21 e.printStackTrace();22 }23 System.out.println("选手" Thread.currentThread().getName() "起跑");24 },"play[" i "]");25 player[i].start();26 }27 28 }29 30 }
对于CountDownLatch和CyclicBarrier两个类,我们可以看到CountDownLatch类是一个类似于集合点的概念,很多个线程做完事情后等待其他线程完成,全部线程完成之后再恢复运行。不同的是CountDownLatch类需要自己调用countDown()方法减少一个计数,然后调用await()方法即可,而CyclicBarrier则直接调用await()方法即可。
所以从上面来看,CountDownLatch更倾向于多个线程合作的情况,等你所有的东西都准备好了,我这边就自动执行了,而CyclicBarrier则是我们都在一个地方等你,大家到齐了再一起执行。
来源:https://www.icode9.com/content-4-643801.html联系客服