打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
CAS和AQS

CAS的概念:

  CAS的全称为Compare And Swap,直译就是比较交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,在intel的CPU中,使用的是cmpxchg指令,就是说CAS是靠硬件实现的,从而在硬件层面提升效率。

CAS的原理:

  CAS (compareAndSwap),中文叫比较交换,一种无锁原子算法。过程是这样:它包含 3 个参数 CAS(V,E,N),V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做两个更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。CAS 操作时抱着乐观的态度进行的,它总是认为自己可以成功完成操作。

AQS的概念:

  AQS(abstractQueueSychronizer)同步发生器。通过内置得到FIFO同步队列来完成线程争夺资源的管理工作。

AtomicInteger的用法:

 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 }

AtomicStampedReference的用法:

 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 }

通过实现Java的Lock接口来模拟一个锁的实现功能:

 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 }

使用JDK提供的可重入性锁(ReentrantLock类):

  可重入性:同一个锁当有多个同一资源进行占有的时候,直接分配给这个线程。

 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 }

使用CountDownLatch(int count)类来实现航空公司卖票的示例:

  构造器中的计数值(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。

  基本原理:每个线程执行时都会碰到一个屏障,直到所有线程执行结束,然后屏障便会打开,使所有线程继续往下执行。

  在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和CyclicBarrier两个类,我们可以看到CountDownLatch类是一个类似于集合点的概念,很多个线程做完事情后等待其他线程完成,全部线程完成之后再恢复运行。不同的是CountDownLatch类需要自己调用countDown()方法减少一个计数,然后调用await()方法即可,而CyclicBarrier则直接调用await()方法即可。

  所以从上面来看,CountDownLatch更倾向于多个线程合作的情况,等你所有的东西都准备好了,我这边就自动执行了,而CyclicBarrier则是我们都在一个地方等你,大家到齐了再一起执行。

来源:https://www.icode9.com/content-4-643801.html
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Java 如何实现线程间通信?
说说 Java 线程间通信
Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
【Java并发编程实战】-----“J.U.C”:Phaser
CountDownLatch&CyclicBarrier&Semaphore
面试官:说说CountDownLatch,CyclicBarrier,Semaphore的原理?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服