基于同步器的合并接口实现

来源:互联网 发布:淘宝的渠道管理在哪里 编辑:程序博客网 时间:2024/06/06 03:01

       在上篇《高并发下合并接口请求》中是简单的基于计数器来实现,每个线程都会以一定的时间(10毫秒)间隔检查是否有接口返回。其中的延时可以把间隔设置的非常小,但所有线程的调度还是有些浪费CPU资源。近两天看Tomcat源码时无意发现了一个叫AbstractQueuedSynchronizer的同步器,这个是JDK并发包下提供的工具类,如著名的ReentrantLock就是基于此实现。

       AbstractQueuedSynchronizer提供了一个基于FIFO队列,可以用于构建锁或者其他相关同步类的基础框架。使用的方法是继承,子类通过继承同步器并需要实现它的方法来管理其状态,一般子类推荐被定义为自定义同步类的内部类。该同步器即可以作为排他模式也可以作为共享模式,当它被定义为一个排他模式时,其他线程对其的获取就被阻止,而共享模式对于多个线程获取都可以成功。

       同步器中FIFO队列的元素Node就是保存着线程引用和线程状态的容器,每个线程对同步器的访问,都可以看做是队列中的一个节点。当前线程如果获取不到锁,将当前线程构造成节点Node并加入sync队列尾,进入队列的每个线程都是一个节点Node,从而形成了一个双向队列。每个节点都是一个线程在进行自旋,判断依据就是自己是否是首节点的后继并且能够获资源,在释放时仅仅需要将资源还回去,然后通知一下后继节点并将其唤醒。

       AbstractQueuedSynchronizer已经实现了线程管理,只需要继承它实现一个自定义的同步器即可。该同步器特征为:同一并发时刻第一个线程为独占锁,后续线程为共享锁(共享第一个线程的执行结果),最后一个线程清除缓存结果。可以在同步器中加个计数器、缓存变量以及配合自身的状态来实现。

       当调用countUpOrAwait获取锁时,先计数器加1,并依次尝试获取共享锁、独占锁。获取共享只要判断下状态即可,这里的需求是合并接口调用,即使共享锁到独占锁过渡的时候,如果获取到了缓存数据则直接返回,如果获取的缓存数据为空则只要重试一次即可。主要是获取独占锁时要调用compareAndSetState来原子的尝试修改状态,锁状态定义3个值,分别是:1没有请求、0独占、-1共享。当独占线程执行完成后需调用setValue来把结果缓存到Sync中,这样一来也不用再单独定义一个缓存Map。最后就是调用countDown,计算数器减1并释放资源,如果是最后一个线程则还需清空关联的缓存变量。    

--------------------------------

相关代码为:synchplay/easyJCommon