济南营销型网站建设,合肥装修公司哪家口碑最好,装饰公司响应式网站建设案例,搭建 网站 实例JUC锁与AQS技术【我的Android开发技术】
AQS原理
AQS就是一个同步器#xff0c;要做的事情就相当于一个锁#xff0c;所以就会有两个动作#xff1a;一个是获取#xff0c;一个是释放。获取释放的时候该有一个东西来记住他是被用还是没被用#xff0c;这个东西就是一个状…JUC锁与AQS技术【我的Android开发技术】
AQS原理
AQS就是一个同步器要做的事情就相当于一个锁所以就会有两个动作一个是获取一个是释放。获取释放的时候该有一个东西来记住他是被用还是没被用这个东西就是一个状态。如果锁被获取了也就是被用了还有很多其他的要来获取锁总不能给全部拒绝了这时候就需要他们排队这里就需要一个队列。这大概就清楚了AQS的主要构成了
获取和释放两个动作同步状态原子操作阻塞队列
Lock特性
锁机制用于保证操作的原子性、可见性、顺序性。 JDK1.5的concurrent并发包 中新增了 Lock接口以及相关实现类来实现锁功能 最明显的特性就是需要显式的申请锁和释放锁。比synchronized更加灵活。
显示锁的释放锁的操作一定要放到finally块中否则可能会因为异常导致锁永远无法释放这是显式锁最明显的缺点。
1.1.显示加锁、解锁
synchronized 关键字是自动进行加锁、解锁的而Lock的具体实现需要 lock() 和 unlock()方法配合 try/finally 语句块来完成来手动加锁、解锁。
1.1.可重入
像 synchronized 和 ReentrantLock 都是可 重入锁 可重入性表明 了锁的分配机制是 基于线程的分配 而 不是基于方法调用 的分配。
可重入锁又名 递归锁 即一个线程得到一个对象锁后再次请求该对象锁是永远可以拿到锁的。在 Java 中线程获得对象锁的操作是 以线程为单位的 而不是以方法调用为单位的。但 获取锁和释放锁必须要成对出现 。如 ReentrantLock 它是基于 AQS(AbstractQueueSyncronized) 实现的, AQS 是基于 volitale 和 CAS 实现的 其中 AQS 中维护一个 valitale 类型的变量 state 来做一个可重入锁的重入次数加锁和释放锁也是围绕这个变量来进行的 。
1.2.可响应中断
当线程因为获取锁而进入 阻塞状态 外部是可以中断该线程的 调用方通过捕获nterruptedException可以捕获中断如 ReentrantLock 中的 lockInterruptibly() 方法 可以使线程在被 阻塞 时响应中断 假设 线程1 通过 lockInterruptibly() 方法获取到一个可重入锁并执行一个长时间的任务另一个 线程2 通过 interrupt() 方法 就可以立刻 打断 线程1的执行 来获取 线程1 持有的那个可重入锁。而通过 ReentrantLock 的 lock() 方法或者 Synchronized 持有锁的线程是不会响应其他线程的 interrupt() 方法的直到该方法主动释放锁之后才会响应 interrupt() 方法。 1.3.可设置等待超时时间
synchronized 关键字无法设置锁的超时时间如果一个获得锁的线程内部发生死锁那么其他线程就会一直进入阻塞状态 而 Lock的具体实现可以 设置线程获取锁的等待超时时间 通过 方法返回值 判断是否成功获取锁来 避免死锁
1.4.锁的公平性
提供公平锁和非公平锁2种选择。 公平锁 默认 线程将按照他们发出 发出申请锁的顺序 来获取锁先进先出 不允许插队 非公平锁 允许插队 当一个线程请求获取锁时如果这个锁是 可用 的那这个线程将 跳过所在队列里等待线程并获得锁。 如 synchronized 关键字是一 种非公平锁 先抢到锁的线程先执行。而 ReentrantLock的构造方法中允许设置 true/false 来实现公平、非公平锁 如果设置为 true 则线程获取锁要遵循 “先来后到” 的规则每 次都会构造一个 线程 Node 然后到双向链表的 尾巴后面排队等待前面的 Node 释放锁资源。 考虑这么一种情况A线程持有锁B线程请求这个锁因此B线程被挂起A线程释放这个锁时B线程将被唤醒因此再次尝试获取锁 与此同时C线程也请求获取这个锁那么C线程很可能在B线程被完全唤醒之前 获得、使用以及释放 这个锁。这是种双赢的局面B获取锁的时刻B被唤醒后才能获取锁并没有推迟C更早地获取了锁并且吞吐量也获得了提高。 在大多数情况下非公平锁的性能要高于公平锁的性能。 另外这个公平性是针对 线程 而言的不能依赖此来实现业务上的公平性应该由开发者自己控制比如通过 FIFO队列 来保证公平。 1.5.读写锁 允许读锁和写锁分离读锁与写锁互斥但是多个读锁可以共存 即一个资源可以被多个读操作访问或者被一个写操作访问但两者不能同时进行 。适用于 读远大于写 的场景 即读读共享、写写互斥、读写互斥 关于读写锁的一些知识 1重入方面其内部的写锁可以获取读锁但是反过来读锁想要获得写锁则永远都不要想。 2写锁可以降级为读锁顺序是 先获得写锁再获得读锁然后释放写锁 ,这时候线程将保持读锁的持有。反过来读锁想要升级为写锁则不行 3 读锁被线程持有时排斥任何的写锁而线程持有写锁则是完全的互斥.这一特性最为重要 对于 读多写少 的场景使用此类才可以提高并发量。 4不管是读锁还是写锁都支持 响应中断 5写锁支持Condition且用于与ReentrantLock一样 而读锁则不能使用Condition 否则抛出UnsupportedOperationException异常。
1.6.丰富的API
提供了多个方法来获取锁相关的信息可以帮助开发者监控和排查问题
isFair() 判断锁是否是公平锁isLocked() 判断锁是否被任何线程获取了isHeldByCurrentThread() 判断锁是否被当前线程获取了hasQueuedThreads() 判断是否有线程在等待该锁etHoldCount() 查询当前线程占有lock锁的次数getQueueLength() 获取正在等待此锁的线程数
1.7.常用方法
void lock() 在线程获取锁时如果锁已被其他线程获取则进行 等待
Lock lock new ReentrantLock();//获取锁
lock.lock();
try{//获取到了被本锁保护的资源处理任务//捕获异常
}finally{lock.unlock(); //释放锁
}boolean tryLock() 尝试获取锁 如果当前锁没有被其他线程占用则获取成功返回 true否则返回 false 我们可以根据是否能获取到锁来决定后续程序的行为。该方法会立即返回在拿不到锁时也不会一直等待通常我们用 if 语句 判断 tryLock() 的返回结果 根据是否获取到锁来执行不同的业务逻辑 使用方法如下。 Lock lock new ReentrantLock();//获取锁
//如果能获取到锁
if(lock.tryLock()) {try{//处理任务}finally{lock.unlock(); //释放锁}
}
//如果不能获取锁则做其他事情
else {}利用 tryLock() 方法我们还可以解决死锁问题public void tryLock(Lock lock1, Lock lock2) throws InterruptedException {//自旋while (true) {//如果能获取锁1if (lock1.tryLock()) {try {//如果能获取锁2if (lock2.tryLock()) {try {System.out.println(获取到了两把锁完成业务逻辑);return;} finally {//释放锁2lock2.unlock();}}} finally {//释放锁1lock1.unlock();}}//如果不能能获取锁,线程休眠若干秒 else {Thread.sleep(new Random().nextInt(1000));}}}boolean tryLock(long time, TimeUnit unit) 可响应中断并且有超时时间的尝试获取锁 在拿不到锁时会等待一定的时间如果在时间结束后还获取不到锁就会返回 false如果一开始就获取锁或者等待期间内获取到锁则返回 true。
这个方法解决了 lock() 方法容易发生死锁的问题使用 tryLock(long time, TimeUnit unit) 时 在等待了一段指定的超时时间后 线程会主动放弃获取这把锁 避免永久等待。 等待获取锁的期间也可以 随时中断线程 这就避免了死锁的发生。
void lockInterruptibly() 可响应中断的去获取锁 如果这个锁当前是可以获得的那么这个方法会立刻返回但是如果这个锁当前是不能获得的被其他线程占用那么当前线程便会开始 等待 除非它 等到了这把锁或者是在等待的过程中被中断了 否则这个线程便会一直在这里执行这行代码。一句话总结就是 除非当前线程在获取锁期间被 中断 否则便会 一直尝试获取 直到获取到为止。
顾名思义 lockInterruptibly() 是可以 响应中断 的。相比于不能响应中断的 synchronized 锁 lockInterruptibly() 可以让程序更灵活可以在获取锁的同时 保持对中断的响应 。我们可以把这个方法理解为 超时时间是无穷长的 tryLock(long time, TimeUnit unit) 因为 tryLock(long time, TimeUnit unit) 和 lockInterruptibly() 都能响应中断 只不过 lockInterruptibly() 永远不会超时。
public void lockInterruptibly() {try {lock.lockInterruptibly();try {System.out.println(操作资源);} finally {lock.unlock();}} catch (InterruptedException e) {e.printStackTrace();} finally {//释放锁lock.unlock();}}void unlock() 释放当前线程占用的锁, 必须使用finally块保证发生异常时锁一定被释放Condition newCondition() 创建绑定到此 Lock 实例的 Condition实例 用于替代wait()/notify()/notifyAll()方法 实现线程的等待通知机制 Condition对象的await()/signal()/signalAll() 的功能和wait()/notify()/notifyAll()一样 1. Lock接口
在Java5之前要想使用锁来保护共享资源大多数情况是使用synchronized关键字。在Java5之后并发包里增加了Lock接口及其实现类来提供锁的功能。
Lock接口与synchronized有许多区别。synchronized修饰在方法体或者代码块上可以隐式地获取和释放锁并且锁地获取和释放操作被固化。但是Lock接口可以显示地获取和释放锁提高了锁的可操作性。并且Lock接口支持可中断锁以及超时锁等synchronized没有的特性。
Lock接口的使用范式如下
1 Lock lock new ReentrantLock();
2 lock.lock();
3 try{
4 //访问临界区
5 }finally{
6 lock.unlock();
7 } Lock接口的实现类能实现锁的功能靠的是聚合AQS的子类作为同步器将提供给用户的锁的操作委托给这个同步器执行。
2. 队列同步器(AQS)
AQS是并发包里同步组件的核心基础可以用来构建锁和其他同步器件。
AQS维护一个int型的同步状态属性利用这个同步状态属性可以实现独占锁和共享锁以及如信号量等同步组件。比如要实现独占锁则同步状态往往初始化为1。另外AQS内部还定义了一个静态内部类Node。Node组成了AQS的同步队列和等待队列Node里保存了线程信息等待状态等。AQS维护同步队列的头结点head和尾结点tail并负责同步队列的操作。
AQS采用模板方法的设计模式提供了独占和共享式获取释放同步状态的方法以及可中断和超时获取同步状态的方法。这些模板方法调用了一些抽象方法。AQS里的抽象方法由继承它的子类根据需要实现。自定义同步组件往往会声明一个继承AQS的静态内部类称为同步器。并将Lock接口提供给用户调用的方法委托给同步器处理。这样做的好处有两个方面第一个是对同步组件的使用者隐藏了实现细节用户只需要调用Lock接口提供的方法就可以获得锁的功能而不知道具体的执行是由同步器来完成。第二个是向同步组件的开发者提供了便捷的开发接口隐藏了底层操作系统的线程管理等细节。
2.1 AQS的同步状态操作
利用AQS设计同步组件的关键在于同步状态。AQS提供了三个方法保证线程安全的修改同步状态 1 private volatile int state;2 protected final void setState(int newState) {3 state newState;4 }5 protected final int getState() {6 return state;7 }8 protected final boolean compareAndSetState(int expect, int update) {9 // See below for intrinsics setup to support this
10 return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
11 } 可以看到同步状态的线程安全是基于volatile的可见性和禁止重排序以及利用CAS原子性替换。
2.2 AQS的实现分析
2.2.1 同步队列
AQS里的同步队列(FIFO队列双向链表)用来完成同步状态的管理。当线程获取同步状态失败时其线程引用等待状态会被封装成Node并被加入到同步队列同时AQS会阻塞该线程直到被同步队列首节点的线程唤醒并尝试获取同步状态。
AQS维护同步队列的头结点head和尾结点tail当线程获取同步状态失败时会被加入到同步队列的尾部。
当线程获取同步状态失败时会被构造成Node并插入同步队列尾部由于同一时刻可能与多个线程要被同时插入到尾部为了避免出现类似HashMap在多线程环境下链表拉链可能拉成环的情况AQS使用CAS的方式确保插入尾结点线程安全。
AQS通过addWaiter方法构造Node结点并将其插入到同步队列尾部代码如下。首先尝试进行一次快速的插入利用CAS检查实际尾结点和线程认为的尾结点是否一致相同则修改结点指针完成插入。如果快速插入失败则进入enq方法自旋加CAS不断尝试插入的同步队列尾部直到成功。 1 private Node addWaiter(Node mode) {2 Node node new Node(Thread.currentThread(), mode);3 // Try the fast path of enq; backup to full enq on failure4 Node pred tail;5 if (pred ! null) {6 node.prev pred;7 if (compareAndSetTail(pred, node)) {8 pred.next node;9 return node;
10 }
11 }
12 enq(node);
13 return node;
14 } AQS的头结点通过unpartSuccessor方法释放同步状态并利用LockSupport的方法唤醒其后继结点这一过程没有使用CAS。头结点将其引用修改尾其后继结点并断开与后继结点的连接完成释放。
2.2.2 独占式同步状态获取与释放
调用AQS的acquire(int)方法可以独占式的获取同步状态该方法的逻辑简单来说是这样的首先调用tryAcquire(int)方法线程安全地获取同步状态如果获取同步状态失败则构造Node结点并将其加入等待队列中。在等待队列里的线程不断地自旋检查其前驱是否是首结点并且能否获取同步状态如果前驱不是首结点或者获取同步状态失败则修改结点的等待状态并且阻塞结点保存的线程直到首结点释放同步状态并唤醒后继结点让等待的线程成功获取到同步状态这个自旋地过程是不响应中断的。 1 public final void acquire(int arg) {2 if (!tryAcquire(arg) 3 acquireQueued(addWaiter(Node.EXCLUSIVE), arg))4 selfInterrupt();5 }6 final boolean acquireQueued(final Node node, int arg) {7 boolean failed true;8 try {9 boolean interrupted false;
10 for (;;) {
11 final Node p node.predecessor();
12 //如果前驱不是首结点或者前驱是首结点但是获取同步状态失败
13 //则被阻塞
14 if (p head tryAcquire(arg)) {
15 setHead(node);
16 p.next null; // help GC
17 failed false;
18 return interrupted;
19 }
20 if (shouldParkAfterFailedAcquire(p, node)
21 parkAndCheckInterrupt())
22 interrupted true;
23 }
24 } finally {
25 if (failed)
26 cancelAcquire(node);
27 }
28 } 调用AQS的release(int)方法可以释放独占式同步状态。该方法的逻辑简单如下首先调用treRelease释放同步状态如果释放成功则唤醒其后继结点并且将头结点引用修改成其后继。由于获取到同步状态只能由一个线程故这一操作不需要CAS来保证线程安全。
1 public final boolean release(int arg) {
2 if (tryRelease(arg)) {
3 Node h head;
4 if (h ! null h.waitStatus ! 0)
5 unparkSuccessor(h);
6 return true;
7 }
8 return false;
9 }2.2.3 共享式同步状态获取与释放
共享式访问同步状态与独占式访问最大的区别在于共享式访问不会阻塞其他共享式访问同步状态但是独占式只能有一个线程进入访问其他线程无论是共享还是独占都会被阻塞。
调用AQS的acquireShared(int)方法来共享式获取同步状态首先调用tryAcquireShared(int)方法尝试获取共享同步状态当该方法返回值大于等于0表示获取成功小于0获取失败则调用doAcquireShared(int)方法。doAcquireShared方法简单来说就是构造Node结点加入到同步队列尾部然后进入自旋过程。在自选过程里当前驱是首结点时再次尝试获取同步状态获取成功则退出自旋否则被阻塞。 1 public final void acquireShared(int arg) {2 if (tryAcquireShared(arg) 0)3 doAcquireShared(arg);4 }5 private void doAcquireShared(int arg) {6 //构造Node结点构造状态为共享7 final Node node addWaiter(Node.SHARED);8 boolean failed true;9 try {
10 boolean interrupted false;
11 //自旋尝试获取共享式同步状态
12 for (;;) {
13 final Node p node.predecessor();
14 //只有当前驱是首结点时才会尝试获取同步状态
15 if (p head) {
16 int r tryAcquireShared(arg);
17 if (r 0) {
18 setHeadAndPropagate(node, r);
19 p.next null; // help GC
20 if (interrupted)
21 selfInterrupt();
22 failed false;
23 return;
24 }
25 }
26 //前驱不是首结点或者获取同步状态失败被阻塞直到首结点释放同步状态将其唤醒
27 if (shouldParkAfterFailedAcquire(p, node)
28 parkAndCheckInterrupt())
29 interrupted true;
30 }
31 } finally {
32 if (failed)
33 cancelAcquire(node);
34 }
35 }[ 调用AQS的releaseShared(int)方法释放共享式同步状态该方法首先调用tryReleaseShared(int)尝试释放同步状态如果释放成功则调用doReleaseShared()方法唤醒后续处于等待的结点这过程是采用CAS来保证线程安全因为共享式同步状态往往有多个线程同时持有同步状态。
2.2.4 等待队列
在总结AQS等待队列实现之前首先要总结一下Condition接口相关的知识。
任意一个Java对象都有waitnotifynotifyAll方法利用这些方法可以在线程不满足某些执行条件时进入等待状态直到其他线程将其唤醒。Java对象自带的这些监视器方法配合synchronized锁可以实现等待通知范式但是相比之下利用Condition接口提供的方法也能做到一样的功能并且更加灵活。且Condition接口与对象监视器方法的不同点有Condition支持中断屏蔽等待特定时间等待以及最重要的对象监视器只有一个等待队列但是利用Condition可以支持多个条件多个等待队列。
Condition的使用也非常简单下面是一个简单的示例 1 Lock lock new ReentrantLock();2 Condition c1 lock.newCondition();3 4 public void sample() throw InterruptedException{5 //获取锁6 lock.lock();7 try{8 //只有获取锁成功才能调用条件对象的相关方法调用之后线程释放锁并被构造成Node结点9 //进入Condition的等待队列
10 condition.await();
11 }finally{
12 lock.unlock();
13 }
14 }[ 获取到锁的线程调用await方法会释放锁然后进入等待队列直到其他线程调用signal方法将其唤醒。Condition对象的创建依赖于Lock对象。
ConditionObject是AQS的内部类实现了Condition接口。ConditionObject对象维护等待队列的头结点和尾结点。等待队列的结点类型与同步队列结点一样都是Node。AQS里可以被多个ConditionObject依赖故相比于synchronized的对象监视器只能有一个等待队列基于AQS的同步组件可以有多个等待队列。
当获取到锁的线程调用await方法时将会释放锁并进入等待状态具体过程简单来说如下首先检查线程中断状态如果被中断抛出中断异常。之后调用addConditionWaiter()方法将线程引用以及等待状态构造成Node结点并释放锁这部分过程没有CAS来确保线程安全因为这时候线程还持有锁。最后进入自旋过程不断检查引用了自己的结点是否被加入到同步队列中参与同步状态的获取如果没有被加入同步队列则被阻塞。最后当发现自己被加入到同步队列时退出自旋过程调用aquiredQueued方法自旋获取同步状态。 1 public final void await() throws InterruptedException {2 if (Thread.interrupted())3 throw new InterruptedException();4 //将线程引用以及等待状态构造成新的Node结点添加到等待队列尾部这个过程由锁确保线程安全5 Node node addConditionWaiter();6 //释放锁并唤醒同步队列的后继结点7 int savedState fullyRelease(node);8 int interruptMode 0;9 //自旋检查自己是否被加入到同步队列没有则被阻塞
10 while (!isOnSyncQueue(node)) {
11 LockSupport.park(this);
12 if ((interruptMode checkInterruptWhileWaiting(node)) ! 0)
13 break;
14 }
15 //当发现自己被加入到同步队列退出自旋并参与到锁的获取
16 if (acquireQueued(node, savedState) interruptMode ! THROW_IE)
17 interruptMode REINTERRUPT;
18 if (node.nextWaiter ! null) // clean up if cancelled
19 unlinkCancelledWaiters();
20 if (interruptMode ! 0)
21 reportInterruptAfterWait(interruptMode);
22 }[](javascript:void(0); 当调用ConditionObject对象的signal方法时过程简单描述如下首先检查并确保调用siganl方法的线程是获取锁的线程。之后调用doSignal方法循环尝试修改等待队列头结点指向其后继并断开原本首结点与其后继的指针再进入自旋加CAS过程尝试将其从等待队列转移到同步队列尾部这个过程没有构造新的Node。当被加入到同步队列成功时当前线程唤醒刚被插入到同步队列尾部的结点的线程被唤醒的线程退出wait方法里的检查是否在同步队列循环参与到自旋获取锁的过程。 1 public final void signal() {2 if (!isHeldExclusively())3 throw new IllegalMonitorStateException();4 Node first firstWaiter;5 if (first ! null)6 doSignal(first);7 }8 private void doSignal(Node first) {9 do {
10 if ( (firstWaiter first.nextWaiter) null)
11 lastWaiter null;
12 first.nextWaiter null;
13 } while (!transferForSignal(first)
14 (first firstWaiter) ! null);
15 }以上就是Android技术中的JUC锁与AQS技术这些东西可以帮助你在 APP启动优化 上面更好的铺垫有关更多Android技术或者性能优化方面的学习大家可以点击《Android核心技术类目》查看一些技术资料里面内容概括了Android大部分的高工进阶知识点。
最后
AQS究竟是做什么的
你该如何保证多个线程访问该对象时正确地进行阻塞等待正确地被唤醒
关于这个问题java的设计者认为应该是一套通用的机制因此将一套线程阻塞等待以及被唤醒时锁分配的机制称之为AQS 全称 AbstractQuenedSynchronizer 中文名即抽象的队列式同步器。基于AQS实现了例如ReentenLock之类的经典JUC类。 文章转载自: http://www.morning.prysb.cn.gov.cn.prysb.cn http://www.morning.shxmr.cn.gov.cn.shxmr.cn http://www.morning.fwzjs.cn.gov.cn.fwzjs.cn http://www.morning.tfcwj.cn.gov.cn.tfcwj.cn http://www.morning.xpmhs.cn.gov.cn.xpmhs.cn http://www.morning.qzxb.cn.gov.cn.qzxb.cn http://www.morning.prhqn.cn.gov.cn.prhqn.cn http://www.morning.prddj.cn.gov.cn.prddj.cn http://www.morning.trsmb.cn.gov.cn.trsmb.cn http://www.morning.chehb.com.gov.cn.chehb.com http://www.morning.yqsr.cn.gov.cn.yqsr.cn http://www.morning.tdnbw.cn.gov.cn.tdnbw.cn http://www.morning.phwmj.cn.gov.cn.phwmj.cn http://www.morning.psdsk.cn.gov.cn.psdsk.cn http://www.morning.pamdeer.com.gov.cn.pamdeer.com http://www.morning.ytmx.cn.gov.cn.ytmx.cn http://www.morning.kpzbf.cn.gov.cn.kpzbf.cn http://www.morning.plkrl.cn.gov.cn.plkrl.cn http://www.morning.fplwz.cn.gov.cn.fplwz.cn http://www.morning.rqfnl.cn.gov.cn.rqfnl.cn http://www.morning.fktlr.cn.gov.cn.fktlr.cn http://www.morning.fwllb.cn.gov.cn.fwllb.cn http://www.morning.pdmsj.cn.gov.cn.pdmsj.cn http://www.morning.qscsy.cn.gov.cn.qscsy.cn http://www.morning.txtzr.cn.gov.cn.txtzr.cn http://www.morning.jrslj.cn.gov.cn.jrslj.cn http://www.morning.hxhrg.cn.gov.cn.hxhrg.cn http://www.morning.rxwnc.cn.gov.cn.rxwnc.cn http://www.morning.pzcqz.cn.gov.cn.pzcqz.cn http://www.morning.kztts.cn.gov.cn.kztts.cn http://www.morning.nfks.cn.gov.cn.nfks.cn http://www.morning.yrmpz.cn.gov.cn.yrmpz.cn http://www.morning.kpcky.cn.gov.cn.kpcky.cn http://www.morning.qtzqk.cn.gov.cn.qtzqk.cn http://www.morning.tbhlc.cn.gov.cn.tbhlc.cn http://www.morning.dgckn.cn.gov.cn.dgckn.cn http://www.morning.c7498.cn.gov.cn.c7498.cn http://www.morning.sqhlx.cn.gov.cn.sqhlx.cn http://www.morning.xsqbx.cn.gov.cn.xsqbx.cn http://www.morning.rfkyb.cn.gov.cn.rfkyb.cn http://www.morning.bpmdz.cn.gov.cn.bpmdz.cn http://www.morning.httzf.cn.gov.cn.httzf.cn http://www.morning.zrqs.cn.gov.cn.zrqs.cn http://www.morning.fbnsx.cn.gov.cn.fbnsx.cn http://www.morning.zsrdp.cn.gov.cn.zsrdp.cn http://www.morning.qrqg.cn.gov.cn.qrqg.cn http://www.morning.tnhmp.cn.gov.cn.tnhmp.cn http://www.morning.qrnbs.cn.gov.cn.qrnbs.cn http://www.morning.gkdqt.cn.gov.cn.gkdqt.cn http://www.morning.gwqkk.cn.gov.cn.gwqkk.cn http://www.morning.ccphj.cn.gov.cn.ccphj.cn http://www.morning.yrkdq.cn.gov.cn.yrkdq.cn http://www.morning.wxfjx.cn.gov.cn.wxfjx.cn http://www.morning.ypwlb.cn.gov.cn.ypwlb.cn http://www.morning.nkyqh.cn.gov.cn.nkyqh.cn http://www.morning.pngph.cn.gov.cn.pngph.cn http://www.morning.xknsn.cn.gov.cn.xknsn.cn http://www.morning.rcqyk.cn.gov.cn.rcqyk.cn http://www.morning.bpmtj.cn.gov.cn.bpmtj.cn http://www.morning.qygfb.cn.gov.cn.qygfb.cn http://www.morning.zdfrg.cn.gov.cn.zdfrg.cn http://www.morning.zyytn.cn.gov.cn.zyytn.cn http://www.morning.nkrmh.cn.gov.cn.nkrmh.cn http://www.morning.nmfxs.cn.gov.cn.nmfxs.cn http://www.morning.yslfn.cn.gov.cn.yslfn.cn http://www.morning.qwbht.cn.gov.cn.qwbht.cn http://www.morning.dnbkz.cn.gov.cn.dnbkz.cn http://www.morning.blznh.cn.gov.cn.blznh.cn http://www.morning.tkrdg.cn.gov.cn.tkrdg.cn http://www.morning.bgxgq.cn.gov.cn.bgxgq.cn http://www.morning.mhpmw.cn.gov.cn.mhpmw.cn http://www.morning.zjcmr.cn.gov.cn.zjcmr.cn http://www.morning.txzqf.cn.gov.cn.txzqf.cn http://www.morning.pnjsl.cn.gov.cn.pnjsl.cn http://www.morning.ishoufeipin.cn.gov.cn.ishoufeipin.cn http://www.morning.krgjc.cn.gov.cn.krgjc.cn http://www.morning.lfmwt.cn.gov.cn.lfmwt.cn http://www.morning.wrlff.cn.gov.cn.wrlff.cn http://www.morning.ymtbr.cn.gov.cn.ymtbr.cn http://www.morning.wtlyr.cn.gov.cn.wtlyr.cn