当前位置: 首页 > news >正文

安卓市场网站建设健康河北app下载二维码

安卓市场网站建设,健康河北app下载二维码,网站推广app软件,自定义页面wordpress哈喽大家好#xff0c;我是阿Q。 最近是上班忙项目#xff0c;下班带娃#xff0c;忙的不可开交#xff0c;连摸鱼的时间都没有了。今天趁假期用图解的方式从源码角度给大家说一下ReentrantLock加锁解锁的全过程。系好安全带#xff0c;发车了。 简单使用 在聊它的源码…哈喽大家好我是阿Q。 最近是上班忙项目下班带娃忙的不可开交连摸鱼的时间都没有了。今天趁假期用图解的方式从源码角度给大家说一下ReentrantLock加锁解锁的全过程。系好安全带发车了。 简单使用 在聊它的源码之前我们先来做个简单的使用说明。当我在IDEA中创建了一个简单的Demo之后它会给出以下提示 提示文字 在使用阻塞等待获取锁的方式中必须在try代码块之外并且在加锁方法与try代码块之间没有任何可能抛出异常的方法调用避免加锁成功后在finally中无法解锁。 1、如果在lock方法与try代码块之间的方法调用抛出异常那么无法解锁造成其它线程无法成功获取锁。2、如果lock方法在try代码块之内可能由于其它方法抛出异常导致在finally代码块中unlock对未加锁的对象解锁它会调用AQS的tryRelease方法取决于具体实现类抛出IllegalMonitorStateException异常。3、在Lock对象的lock方法实现中可能抛出unchecked异常产生的后果与说明二相同。 java.concurrent.LockShouldWithTryFinallyRule.rule.desc 还举了两个例子正确案例如下 Lock lock new XxxLock(); // ... lock.lock(); try {doSomething();doOthers(); } finally {lock.unlock(); }错误案例如下 Lock lock new XxxLock(); // ... try {// 如果在此抛出异常会直接执行 finally 块的代码doSomething();// 不管锁是否成功finally 块都会执行lock.lock();doOthers();} finally {lock.unlock(); } AQS 上边的案例中加锁调用的是lock()方法解锁用的是unlock()方法而通过查看源码发现它们都是调用的内部静态抽象类Sync的相关方法。 abstract static class Sync extends AbstractQueuedSynchronizer Sync 是通过继承AbstractQueuedSynchronizer来实现的没错AbstractQueuedSynchronizer就是AQS的全称。AQS内部维护着一个FIFO的双向队列CLHReentrantLock也是基于它来实现的先来张图感受下。 Node 属性 //此处是 Node 的部分属性 static final class Node {//排他锁标识static final Node EXCLUSIVE null;//如果带有这个标识证明是失效了static final int CANCELLED 1;//具有这个标识说明后继节点需要被唤醒static final int SIGNAL -1;//Node对象存储标识的地方volatile int waitStatus;//指向上一个节点volatile Node prev;//指向下一个节点volatile Node next;//当前Node绑定的线程volatile Thread thread;//返回前驱节点即上一个节点如果前驱节点为空抛出异常final Node predecessor() throws NullPointerException {Node p prev;if (p null)throw new NullPointerException();elsereturn p;} }对于里边的waitStatus属性我们需要做个解释非常重要 CANCELLED(1)当前节点取消获取锁。当等待超时或被中断(响应中断)会触发变更为此状态进入该状态后节点状态不再变化SIGNAL(-1)后面节点等待当前节点唤醒CONDITION(-2)Condition中使用当前线程阻塞在Condition如果其他线程调用了Condition的signal方法这个结点将从等待队列转移到同步队列队尾等待获取同步锁PROPAGATE(-3)共享模式前置节点唤醒后面节点后唤醒操作无条件传播下去0中间状态当前节点后面的节点已经唤醒但是当前节点线程还没有执行完成 AQS 属性 // 头结点 private transient volatile Node head;// 尾结点 private transient volatile Node tail;//0-1 拿到锁大于0 说明当前已经有线程占用了锁资源 private volatile int state;今天我们先简单了解下AQS的构造以帮助大家更好的理解ReentrantLock至于深层次的东西先不做展开 加锁 对AQS的结构有了基本了解之后我们正式进入主题——加锁。从源码中可以看出锁被分为公平锁和非公平锁。 /*** 公平锁代码*/ final void lock() {acquire(1); }/*** 非公平锁代码*/ final void lock() {if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1); }初步查看代码发现非公平锁似乎包含公平锁的逻辑所以我们就从“非公平锁”开始。 非公平锁 final void lock() {//通过 CAS 的方式尝试将 state 从0改为1//如果返回 true代表修改成功获得锁资源;//如果返回false代表修改失败未获取锁资源if (compareAndSetState(0, 1))// 将属性exclusiveOwnerThread设置为当前线程该属性是AQS的父类提供的setExclusiveOwnerThread(Thread.currentThread());elseacquire(1); }compareAndSetState()底层调用的是unsafe的compareAndSwapInt该方法是原子操作 假设有两个线程t1、t2在竞争锁资源线程1获取锁资源之后执行setExclusiveOwnerThread操作设置属性值为当前线程t1 此时当t2想要获取锁资源调用lock()方法之后执行compareAndSetState(0, 1)返回false会走else执行acquire()方法。 方法查看 public final void accquire(int arg) {// tryAcquire 再次尝试获取锁资源如果尝试成功返回true尝试失败返回falseif (!tryAcquire(arg) // 走到这代表获取锁资源失败需要将当前线程封装成一个Node追加到AQS的队列中acquireQueued(addWaiter(Node.EXCLUSIVE), arg))// 线程中断selfInterrupt(); }accquire()中涉及的方法比较多我们将进行拆解一个一个来分析顺序tryAcquire() - addWaiter() - acquireQueued() 查看 tryAcquire() 方法 //AQS中 protected boolean tryAcquire(int arg) {//AQS 是基类具体实现在自己的类中实现我们去查看“非公平锁”中的实现throw new UnsupportedOperationException(); }//ReentrantLock 中 protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires); }final boolean nonfairTryAcquire(int acquires) {// 获取当前线程final Thread current Thread.currentThread();//获取AQS 的 state int c getState();// 如果 state 为0代表尝试再次获取锁资源if (c 0) {// 步骤同上通过 CAS 的方式尝试将 state 从0改为1//如果返回 true代表修改成功获得锁资源;//如果返回false代表修改失败未获取锁资源if (compareAndSetState(0, acquires)) {//设置属性为当前线程setExclusiveOwnerThread(current);return true;}}//当前占有锁资源的线程是否是当前线程如果是则证明是可重入操作else if (current getExclusiveOwnerThread()) {//将 state 1int nextc c acquires;//为什么会小于 0 呢因为最大值 1 后会将符号位的0改为1 会变成负数(可参考Integer.MAX_VALUE 1)if (nextc 0) // overflow//加1后小于0超出锁可重入的最大值抛异常throw new Error(Maximum lock count exceeded);//设置 state 状态setState(nextc);return true;}return false; }因为线程1已经获取到了锁此时state为1所以不走nonfairTryAcquire()的if。又因为当前是线程2不是占有当前锁的线程1所以也不会走else if即tryAcquire()方法返回false。 查看 addWaiter() 方法 走到本方法中代表获取锁资源失败。addWaiter()将没有获取到锁资源的线程甩到队列的尾部。 private Node addWaiter(Node mode) {//创建 Node 类并且设置 thread 为当前线程设置为排它锁Node node new Node(Thread.currentThread(), mode);// 获取 AQS 中队列的尾部节点Node pred tail;// 如果 tail null说明是空队列// 不为 null说明现在队列中有数据if (pred ! null) {// 将当前节点的 prev 指向刚才的尾部节点那么当前节点应该设置为尾部节点node.prev pred;// CAS 将 tail 节点设置为当前节点if (compareAndSetTail(pred, node)) {// 将之前尾节点的 next 设置为当前节点pred.next node;// 返回当前节点return node;}}enq(node);return node; }当tail不为空即队列中有数据时我们来图解一下pred!null代码块中的代码。初始化状态如下pred指向尾节点node指向新的节点。 node.prev pred;将node的前驱节点设置为pred指向的节点 compareAndSetTail(pred, node)通过CAS的方式尝试将当前节点node设置为尾结点此处我们假设设置成功则FIFO队列的tail指向node节点。 pred.next node;将pred节点的后继节点设置为node节点此时node节点成功进入FIFO队列尾部。 而当pred为空即队列中没有节点或将node节点设置为尾结点失败时会走enq()方法。我们列举的例子就符合pred为空的情况就让我们以例子为基础继续分析吧。 //现在没人排队我是第一个 || 前边CAS失败也会进入这个位置重新往队列尾巴去塞 private Node enq(final Node node) {//死循环for (;;) {//重新获取tail节点Node t tail;// 没人排队队列为空if (t null) {// 初始化一个 Node 为 head而这个head 没有意义if (compareAndSetHead(new Node()))// 将头尾都指向了这个初始化的Node第一次循环结束tail head;} else {// 有人排队往队列尾巴塞node.prev t;// CAS 将 tail 节点设置为当前节点if (compareAndSetTail(t, node)) {//将之前尾节点的 next 设置为当前节点t.next node;return t;}}} }进入死循环首先会走if方法的逻辑通过CAS的方式尝试将一个新节点设置为head节点然后将tail也指向新节点。可以看出队列中的头节点只是个初始化的节点没有任何意义。 继续走死循环中的代码此时t不为null所以会走else方法。将node的前驱节点指向t通过CAS方式将当前节点node设置为尾结点然后将t的后继节点指向node。此时线程2的节点就被成功塞入FIFO队列尾部。 查看 acquireQueued()方法 将已经在队列中的node尝试去获取锁否则挂起。 final boolean acquireQueued(final Node node, int arg) {// 获取锁资源的标识,失败为 true成功为 falseboolean failed true;try {// 线程中断的标识中断为 true不中断为 falseboolean interrupted false;for (;;) {// 获取当前节点的上一个节点final Node p node.predecessor();//p为头节点尝试获取锁操作if (p head tryAcquire(arg)) {setHead(node);p.next null;// 将获取锁失败标识置为falsefailed false;// 获取到锁资源不会被中断return interrupted;}// p 不是 head 或者 没拿到锁资源if (shouldParkAfterFailedAcquire(p, node) // 基于 Unsafe 的 park方法挂起线程parkAndCheckInterrupt())interrupted true;}} finally {if (failed)cancelAcquire(node);} }这里又出现了一次死循环首先获取当前节点的前驱节点p如果p是头节点(头节点没有意义)说明node是head后的第一个节点此时当前获取锁资源的线程1可能会释放锁所以线程2可以再次尝试获取锁。 假设获取成功证明拿到锁资源了将node节点设置为head节点并将node节点的pre和thread设置为null。因为拿到锁资源了node节点就不需要排队了。 将头节点p的next置为null此时p节点就不在队列中存在了可以帮助GC回收(可达性分析)。failed设置为false表明获取锁成功interrupted为false则线程不会中断。 如果p不是head节点或者没有拿到锁资源会执行下边的代码因为我们的线程1没有释放锁资源所以线程2获取锁失败会继续往下执行。 //该方法的作用是保证上一个节点的waitStatus状态为-1为了唤醒后继节点 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//获取上一个节点的状态,该状态为-1才会唤醒下一个节点。int ws pred.waitStatus;// 如果上一个节点的状态是SIGNAL即-1可以唤醒下一个节点直接返回trueif (ws Node.SIGNAL)return true;// 如果上一个节点的状态大于0说明已经失效了if (ws 0) {do {// 将node 的节点与 pred 的前一个节点相关联并将前一个节点赋值给 prednode.prev pred pred.prev;} while (pred.waitStatus 0); // 一直找到小于等于0的// 将重新标识好的最近的有效节点的 next 指向当前节点pred.next node;} else {// 小于等于0但是不等于-1将上一个有效节点状态修改为-1compareAndSetWaitStatus(pred, ws, Node.SIGNAL);}return false; }只有节点的状态为-1才会唤醒后一个节点如果节点状态未设置默认为0。 图解一下ws0的过程因为ws0的节点为失效节点所以do...while中会重复向前查找前驱节点直到找到第一个ws0的节点为止将node节点挂到该节点上。 我们的pred是头结点且未设置状态所以状态为0会走else。通过CAS尝试将pred节点的waitStatus设置为-1表明node节点需要被pred唤醒。 shouldParkAfterFailedAcquire()返回false继续执行acquireQueued()中的死循环。 步骤和上边一样node的前驱节点还是head继续尝试获取锁。如果线程1释放了锁线程2就可以拿到返回true否则继续调用shouldParkAfterFailedAcquire()因为上一步已经将前驱结点的ws设置为-1了所以直接返回true。 执行parkAndCheckInterrupt()方法通过UNSAFE.park();方法阻塞当前线程2。等以后执行unpark方法的时候如果node是头节点后的第一个节点会进入acquireQueued()方法中走if (p head tryAcquire(arg))的逻辑获取锁资源并结束死循环。 查看cancelAcquire()方法 该方法执行的机率约等于0为什么这么说呢因为针对failed属性只有JVM内部出现问题时才可能出现异常执行该方法。 // node 为当前节点 private void cancelAcquire(Node node) {if (node null)return;node.thread null;// 上一个节点Node pred node.prev;// 节点状态大于0说明节点失效while (pred.waitStatus 0)node.prev pred pred.prev;// 将第一个不是失效节点的后继节点声明出来Node predNext pred.next;// 节点状态变为失效node.waitStatus Node.CANCELLED;// node为尾节点cas设置pred为尾节点if (node tail compareAndSetTail(node, pred)) {//cas将pred的next设置为nullcompareAndSetNext(pred, predNext, null);} else {int ws;// 中间节点// 如果上一个节点不是head 节点if (pred ! head ((ws pred.waitStatus) Node.SIGNAL ||// 前边已经判断了大于0的操作// pred 是需要唤醒后继节点的所以当 waitStatus 不为 -1 时需要将 pred 节点的 waitStatus 设置为 -1 (ws 0 compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) pred.thread ! null) {Node next node.next;if (next ! null next.waitStatus 0)// CAS 尝试将 pred 的 next 指向当前节点的 nextcompareAndSetNext(pred, predNext, next);} else {// head 节点唤醒后继节点unparkSuccessor(node);}node.next node; // help GC} }执行到while时找到前驱节点中最近的有效节点把当前节点node挂到有效节点后边可以过滤掉当前节点前的失效节点。声明出有效节点的第一个后继无效节点predNext并把当前的node节点状态设置为失效状态。 if中的操作如果当前节点是尾节点CAS尝试将最近的有效节点设置为尾节点并将尾节点的next设置为null。 else中的操作 如果pred节点不是头结点即中间节点并且pred的waitStatus为-1或者waitStatus0为了让pred节点能唤醒后继节点需要设置为-1并且pred节点的线程不为空。获取node节点的后继节点如果后继节点有效CAS尝试将pred的next指向node节点的next。 当其他节点来找有效节点的时候走当前node的prev这条线而不是再一个一个往前找可以提高效率。 如果是头结点则唤醒后继节点。 最后将node节点的next指向自己。 解锁 释放锁是不区分公平锁和非公平锁的释放锁的核心是将state由大于 0 的数置为 0。废话不多说直接上代码 //释放锁方法 public void unlock() {sync.release(1); }public final boolean release(int arg) {//尝试释放锁资源如果释放成功返回trueif (tryRelease(arg)) {Node h head;// head 不为空且 head 的 ws 不为0如果为0代表后边没有其他线程挂起if (h ! null h.waitStatus ! 0)// AQS的队列中有 node 在排队并且线程已经挂起// 需要唤醒被挂起的 NodeunparkSuccessor(h);return true;}// 代表释放一次没有完全释放return false; }如果释放锁成功需要获取head节点。如果头结点不为空且waitStatus不为0则证明有node在排队执行唤醒挂起其他node的操作。 查看tryRelease()方法 protected final boolean tryRelease(int releases) {//获取当前锁的状态先进行减1操作代表释放一次锁资源int c getState() - releases;//如果释放锁的线程不是占用锁的线程直接抛出异常if (Thread.currentThread() ! getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free false;// 如果 c 为0 代表锁完全释放了如果不为0代表锁之前重入了一次没释放掉等待下次再次执行时再次判断if (c 0) {// 释放锁标志为 true代表完全释放了free true;// 将占用互斥锁的标识置为 nullsetExclusiveOwnerThread(null);}// 设置 state 状态setState(c);return free; }我们的例子中线程1占用锁资源线程1释放锁之后state为0。进入if操作将释放标志更新为true将FIFO队列的exclusiveOwnerThread标志置为null。 查看unparkSuccessor()方法 用于唤醒AQS中被挂起的线程。 // 注意当前的 node 节点是 head 节点 private void unparkSuccessor(Node node) {//获取 head 的状态int ws node.waitStatus;if (ws 0)// CAS 将 node 的 ws 设置为0代表当前 node 接下来会舍弃compareAndSetWaitStatus(node, ws, 0);// 获取头节点的下一个节点Node s node.next;// 如果下一个节点为null 或者 下一个节点为失效节点需要找到离 head 最近的有效nodeif (s null || s.waitStatus 0) {s null;// 从尾节点开始往前找不等于null且不是node的节点for (Node t tail; t ! null t ! node; t t.prev)// 如果该节点有效则将s节点指向t节点if (t.waitStatus 0)s t;}// 找到最近的node后直接唤醒if (s ! null)LockSupport.unpark(s.thread); }问题解析为什么要从尾结点往前查找呢 因为在addWaiter方法中是先给prev指针赋值最后才将上一个节点的next指针赋值为了避免防止丢失节点或者跳过节点必须从后往前找。 我们举例中head节点的状态为-1通过CAS的方式将head节点的waitStatus设置为0。 我们的头结点的后继节点是线程2所在的节点不为null所以这边会执行unpark操作从上边的acquireQueued()内的parkAndCheckInterrupt()方法继续执行。 private final boolean parkAndCheckInterrupt() {LockSupport.park(this);//返回目标线程是否中断的布尔值:中断返回true不中断返回false且返回后会重置中断状态为未中断return Thread.interrupted(); }因为线程2未中断所以返回false。继续执行acquireQueued()中的死循环 for (;;) {// 获取当前节点的上一个节点final Node p node.predecessor();//p为头节点尝试获取锁操作if (p head tryAcquire(arg)) {setHead(node);p.next null;// 将获取锁失败标识置为falsefailed false;// 获取到锁资源不会被中断return interrupted;}// p 不是 head 或者 没拿到锁资源if (shouldParkAfterFailedAcquire(p, node) // 基于 Unsafe 的 park方法挂起线程parkAndCheckInterrupt())interrupted true; }此时p是头节点且能获取锁成功将exclusiveOwnerThread设置为线程2即线程2 获取锁资源。 将node节点设置为head节点并将node节点的pre和thread设置为null。因为拿到锁资源了node节点就不需要排队了。 将头节点p的next置为null此时p节点就不在队列中存在了可以帮助GC回收(可达性分析)。failed设置为false表明获取锁成功interrupted为false则线程不会中断。 为什么被唤醒的线程要调用Thread.interrupted()清除中断标记 从上边的方法可以看出当parkAndCheckInterrupt()方法返回true时即Thread.interrupted()方法返回了true也就是该线程被中断了。为了让被唤醒的线程继续执行后续获取锁的操作就需要让中断的线程像没有被中断过一样继续往下执行所以在返回中断标记的同时要清除中断标记将其设置为false。 清除中断标记之后不代表该线程不需要中断了所以在parkAndCheckInterrupt()方法返回true时要自己设置一个中断标志interrupted true为的就是当获取到锁资源执行完相关的操作之后进行中断补偿故而需要执行selfInterrupt()方法中断线程。 以上就是我们加锁解锁的图解过程了。最后我们再来说一下公平锁和非公平锁的区别。 区别 前边已经说过了似乎非公平锁包含了公平锁的全部操作。打开公平锁的代码我们发现accquire()方法中只有该方法的实现有点区别。 hasQueuedPredecessors()返回false时才会尝试获取锁资源。该方法代码实现如下 public final boolean hasQueuedPredecessors() {Node t tail; Node h head;Node s;return h ! t ((s h.next) null || s.thread ! Thread.currentThread()); }ht时队列为空表示没人排队可以获取锁资源队列不为空头结点有后继节点不为空且s节点获取锁的线程是当前线程也可以获取锁资源代表锁重入操作 总结 以上就是我们的全部内容了我们在最后再做个总结 代码使用要合乎规范避免加锁成功后在finally中无法解锁理解AQS的FIFO队列以及Node的相关属性尤其注意waitStatus的状态利用图加深对非公平锁源码的理解 跪求一键三连更文很累的不要白嫖我需要一点正反馈。点击名片与我联系希望在这个冷漠的城市里让我们互相温暖。
文章转载自:
http://www.morning.nbqwr.cn.gov.cn.nbqwr.cn
http://www.morning.tjwfk.cn.gov.cn.tjwfk.cn
http://www.morning.lmjkn.cn.gov.cn.lmjkn.cn
http://www.morning.jfbrt.cn.gov.cn.jfbrt.cn
http://www.morning.jgzmr.cn.gov.cn.jgzmr.cn
http://www.morning.bdwqy.cn.gov.cn.bdwqy.cn
http://www.morning.dxxnq.cn.gov.cn.dxxnq.cn
http://www.morning.xhkgl.cn.gov.cn.xhkgl.cn
http://www.morning.ggtgl.cn.gov.cn.ggtgl.cn
http://www.morning.nkpml.cn.gov.cn.nkpml.cn
http://www.morning.nckzt.cn.gov.cn.nckzt.cn
http://www.morning.gl-group.cn.gov.cn.gl-group.cn
http://www.morning.smggx.cn.gov.cn.smggx.cn
http://www.morning.twfdm.cn.gov.cn.twfdm.cn
http://www.morning.zkgpg.cn.gov.cn.zkgpg.cn
http://www.morning.fbhmn.cn.gov.cn.fbhmn.cn
http://www.morning.shawls.com.cn.gov.cn.shawls.com.cn
http://www.morning.xkgyh.cn.gov.cn.xkgyh.cn
http://www.morning.cczrw.cn.gov.cn.cczrw.cn
http://www.morning.bmnm.cn.gov.cn.bmnm.cn
http://www.morning.lrflh.cn.gov.cn.lrflh.cn
http://www.morning.rhdln.cn.gov.cn.rhdln.cn
http://www.morning.yckwt.cn.gov.cn.yckwt.cn
http://www.morning.rhlhk.cn.gov.cn.rhlhk.cn
http://www.morning.tfei69.cn.gov.cn.tfei69.cn
http://www.morning.gjqnn.cn.gov.cn.gjqnn.cn
http://www.morning.sxbgc.cn.gov.cn.sxbgc.cn
http://www.morning.yhplt.cn.gov.cn.yhplt.cn
http://www.morning.bpmtj.cn.gov.cn.bpmtj.cn
http://www.morning.wyppp.cn.gov.cn.wyppp.cn
http://www.morning.wmmjw.cn.gov.cn.wmmjw.cn
http://www.morning.rhpy.cn.gov.cn.rhpy.cn
http://www.morning.rkjz.cn.gov.cn.rkjz.cn
http://www.morning.kyzja.com.gov.cn.kyzja.com
http://www.morning.sqqhd.cn.gov.cn.sqqhd.cn
http://www.morning.pzss.cn.gov.cn.pzss.cn
http://www.morning.rdqzl.cn.gov.cn.rdqzl.cn
http://www.morning.xcnwf.cn.gov.cn.xcnwf.cn
http://www.morning.rtkgc.cn.gov.cn.rtkgc.cn
http://www.morning.jpfpc.cn.gov.cn.jpfpc.cn
http://www.morning.twpq.cn.gov.cn.twpq.cn
http://www.morning.wsrcy.cn.gov.cn.wsrcy.cn
http://www.morning.mgwdp.cn.gov.cn.mgwdp.cn
http://www.morning.ndcf.cn.gov.cn.ndcf.cn
http://www.morning.jxscp.cn.gov.cn.jxscp.cn
http://www.morning.syrzl.cn.gov.cn.syrzl.cn
http://www.morning.dmwck.cn.gov.cn.dmwck.cn
http://www.morning.gypcr.cn.gov.cn.gypcr.cn
http://www.morning.bbtn.cn.gov.cn.bbtn.cn
http://www.morning.yzxlkj.com.gov.cn.yzxlkj.com
http://www.morning.4q9h.cn.gov.cn.4q9h.cn
http://www.morning.gyxwh.cn.gov.cn.gyxwh.cn
http://www.morning.ejknty.cn.gov.cn.ejknty.cn
http://www.morning.lfdmf.cn.gov.cn.lfdmf.cn
http://www.morning.ffdyy.cn.gov.cn.ffdyy.cn
http://www.morning.tfrmx.cn.gov.cn.tfrmx.cn
http://www.morning.mjtft.cn.gov.cn.mjtft.cn
http://www.morning.rnmc.cn.gov.cn.rnmc.cn
http://www.morning.ccdyc.cn.gov.cn.ccdyc.cn
http://www.morning.frxsl.cn.gov.cn.frxsl.cn
http://www.morning.bctr.cn.gov.cn.bctr.cn
http://www.morning.llxqj.cn.gov.cn.llxqj.cn
http://www.morning.ysqb.cn.gov.cn.ysqb.cn
http://www.morning.mflqd.cn.gov.cn.mflqd.cn
http://www.morning.yqmmh.cn.gov.cn.yqmmh.cn
http://www.morning.ptmsk.cn.gov.cn.ptmsk.cn
http://www.morning.dbrpl.cn.gov.cn.dbrpl.cn
http://www.morning.kqzt.cn.gov.cn.kqzt.cn
http://www.morning.gglhj.cn.gov.cn.gglhj.cn
http://www.morning.kmqjx.cn.gov.cn.kmqjx.cn
http://www.morning.qypjk.cn.gov.cn.qypjk.cn
http://www.morning.sbwr.cn.gov.cn.sbwr.cn
http://www.morning.jfjqs.cn.gov.cn.jfjqs.cn
http://www.morning.kzhxy.cn.gov.cn.kzhxy.cn
http://www.morning.wdrxh.cn.gov.cn.wdrxh.cn
http://www.morning.zstbc.cn.gov.cn.zstbc.cn
http://www.morning.smxrx.cn.gov.cn.smxrx.cn
http://www.morning.etsaf.com.gov.cn.etsaf.com
http://www.morning.mkyny.cn.gov.cn.mkyny.cn
http://www.morning.csnmd.cn.gov.cn.csnmd.cn
http://www.tj-hxxt.cn/news/282120.html

相关文章:

  • 给别人做网站怎么赚钱百度官方平台
  • 西安网站制作推广wordpress标签打不开
  • 网站被墙了怎么办wordpress编辑远程图片
  • 在线音乐播放网站模板买域名价格
  • 做网站的如何增加电话量文学网站开发
  • 宜昌小学网站建设寮步网站建设公司
  • 做网站投注员挣钱吗c 网站开发引擎
  • 淘宝客网站需要备案吗中山有哪些网站建立公司
  • 免费商用自媒体图片网站网站点内页还是首页
  • 建设安全协会网站招投标网站开发
  • 如何快速备案网站网站更新维护 怎么做
  • 中小企业网站建设 网络营销番禺人才网最新招聘信息网
  • 沈阳个人网站制作网站优化的要求
  • 抚顺网站建设推荐快速建站系统
  • 湛江网站制作推荐加强政务公开网站建设
  • 企业网站背景颜色做国外衣服的网站
  • 河南省汝州市文明建设网站南昌网站建设兼职
  • 建筑类专业做教育的网站Python视频直播网站开发
  • 企业网站设计seo网页打不开百度网盘
  • aso网站做网站用的书
  • 外贸网站搭建公司多平台网站设计实例
  • 可以兼职做翻译的网站或app百度收录新网站
  • 淘宝客网站如何做推广方案利用access数据库做网站
  • 广州网站建设企业找灵感的网站
  • 网站充值平台怎么做的广水网站建设
  • 有什么好的免费网站做教育宣传语东莞寮步伟易达电子厂
  • 新手做网站详细步骤seo服务建议
  • 网站建设中最基本的决策之一是学网站建设需要多久
  • python做网站好用吗移动端优质网页
  • 58同城网站建设推广排名网站做很久了百度没收录