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

佛山专业做网站的淄博网站网站建设

佛山专业做网站的,淄博网站网站建设,网络营销推广的核心是什么,山西网站建设平台更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 ConcurrentHashMap 类 ConcurrentHashMap 1.7 在JDK1.7中ConcurrentHashMap采用了数组分段锁的方式实现。 Segment(分段锁)-减少锁的粒度 ConcurrentHashMap中的分段锁称为Segment#xff0c;它即类似于… 更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 ConcurrentHashMap 类 ConcurrentHashMap 1.7 在JDK1.7中ConcurrentHashMap采用了数组分段锁的方式实现。 Segment(分段锁)-减少锁的粒度 ConcurrentHashMap中的分段锁称为Segment它即类似于HashMap的结构即内部拥有一个Entry数组数组中的每个元素又是一个链表,同时又是一个ReentrantLockSegment继承了ReentrantLock。 存储结构 Java 7 版本 ConcurrentHashMap 的存储结构如图 ConcurrnetHashMap 由很多个 Segment 组合而每一个 Segment 是一个类似于 HashMap 的结构所以每一个 HashMap 的内部可以进行扩容。 但是 Segment 的个数一旦初始化就不能改变默认 Segment 的个数是 16 个所以可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。 初始化 通过 ConcurrentHashMap 的无参构造 /** * Creates a new, empty map with a default initial capacity (16), * load factor (0.75) and concurrencyLevel (16). */ public ConcurrentHashMap() {this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); }无参构造中调用了有参构造传入了三个参数的默认值他们的值是 /** * 默认初始化容量,这个容量指的是Segment 的大小 */ static final int DEFAULT_INITIAL_CAPACITY 16;/** * 默认负载因子 */ static final float DEFAULT_LOAD_FACTOR 0.75f;/** * 默认并发级别并发级别指的是Segment桶的个数默认是16个并发大小 */ static final int DEFAULT_CONCURRENCY_LEVEL 16; Segment 下面 entryset 数组的大小是用 DEFAULT_INITIAL_CAPACITY/DEFAULT_CONCURRENCY_LEVEL 求出来的。 接着看下这个有参构造函数的内部实现逻辑 SuppressWarnings(unchecked) public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel) {// 参数校验if (!(loadFactor 0) || initialCapacity 0 || concurrencyLevel 0)throw new IllegalArgumentException();// 校验并发级别大小大于 116重置为 65536if (concurrencyLevel MAX_SEGMENTS)concurrencyLevel MAX_SEGMENTS;// Find power-of-two sizes best matching arguments// 2的多少次方int sshift 0;//控制segment数组的大小int ssize 1;// 这个循环可以找到 concurrencyLevel 之上最近的 2的次方值while (ssize concurrencyLevel) {sshift;//代表ssize左移的次数ssize 1;}// 记录段偏移量this.segmentShift 32 - sshift;// 记录段掩码this.segmentMask ssize - 1;// 设置容量 判断初始容量是否超过允许的最大容量if (initialCapacity MAXIMUM_CAPACITY)initialCapacity MAXIMUM_CAPACITY;// c 容量 / ssize 默认 16 / 16 1这里是计算每个 Segment 中的类似于 HashMap 的容量//求entrySet数组的大小这个地方需要保证entrySet数组的大小至少可以存储下initialCapacity的容量假设initialCapacity为33ssize为16那么c2,所以if语句是true那么c3,MIN_SEGMENT_TABLE_CAPACITY初始值是2所以if语句成立那么cap4所以每一个segment的容量初始为4segment为1616*433成立entrySet数组的大小也需要是2的幂次方int c initialCapacity / ssize;if (c * ssize initialCapacity)c;int cap MIN_SEGMENT_TABLE_CAPACITY;//Segment 中的类似于 HashMap 的容量至少是2或者2的倍数while (cap c)cap 1;// create segments and segments[0]// 创建 Segment 数组设置 segments[0]SegmentK,V s0 new SegmentK,V(loadFactor, (int)(cap * loadFactor),(HashEntryK,V[])new HashEntry[cap]);SegmentK,V[] ss (SegmentK,V[])new Segment[ssize];UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]this.segments ss; }总结一下在 Java 7 中 ConcurrnetHashMap 的初始化逻辑 必要参数校验校验并发级别 concurrencyLevel 大小如果大于最大值重置为最大值。无参构造默认值是 16寻找并发级别 concurrencyLevel 之上最近的 2 的幂次方值作为初始化容量大小默认是 16记录 segmentShift 偏移量这个值为【容量 2 的N次方】中的 N在后面 Put 时计算位置时会用到默认是 32 - sshift 28.记录 segmentMask默认是 ssize - 1 16 -1 15初始化 segments[0]默认大小为 2负载因子 0.75扩容阀值是 2*0.751.5插入第二个值时才会进行扩容。 put 操作 接着上面的初始化参数继续查看 put 方法源码 /*** Maps the specified key to the specified value in this table.* Neither the key nor the value can be null.** p The value can be retrieved by calling the ttget/tt method* with a key that is equal to the original key.** param key key with which the specified value is to be associated* param value value to be associated with the specified key* return the previous value associated with ttkey/tt, or* ttnull/tt if there was no mapping for ttkey/tt* throws NullPointerException if the specified key or value is null*/ public V put(K key, V value) {SegmentK,V s;if (value null)throw new NullPointerException();int hash hash(key);// hash 值无符号右移 28位初始化时获得然后与 segmentMask15 做与运算// 其实也就是把高4位与segmentMask1111做与运算// this.segmentMask ssize - 1;//对hash值进行右移segmentShift位计算元素对应segment中数组下表的位置//把hash右移segmentShift相当于只要hash值的高32-segmentShift位右移的目的是保留了hash值的高位。然后和segmentMask与操作计算元素在segment数组中的下表int j (hash segmentShift) segmentMask;//使用unsafe对象获取数组中第j个位置的值后面加上的是偏移量if ((s (SegmentK,V)UNSAFE.getObject // nonvolatile; recheck(segments, (j SSHIFT) SBASE)) null) // in ensureSegment// 如果查找到的 Segment 为空初始化s ensureSegment(j);//插入segment对象return s.put(key, hash, value, false); }/*** Returns the segment for the given index, creating it and* recording in segment table (via CAS) if not already present.** param k the index* return the segment*/ SuppressWarnings(unchecked) private SegmentK,V ensureSegment(int k) {final SegmentK,V[] ss this.segments;long u (k SSHIFT) SBASE; // raw offsetSegmentK,V seg;// 判断 u 位置的 Segment 是否为nullif ((seg (SegmentK,V)UNSAFE.getObjectVolatile(ss, u)) null) {SegmentK,V proto ss[0]; // use segment 0 as prototype// 获取0号 segment 里的 HashEntryK,V 初始化长度int cap proto.table.length;// 获取0号 segment 里的 hash 表里的扩容负载因子所有的 segment 的 loadFactor 是相同的float lf proto.loadFactor;// 计算扩容阀值int threshold (int)(cap * lf);// 创建一个 cap 容量的 HashEntry 数组HashEntryK,V[] tab (HashEntryK,V[])new HashEntry[cap];if ((seg (SegmentK,V)UNSAFE.getObjectVolatile(ss, u)) null) { // recheck// 再次检查 u 位置的 Segment 是否为null因为这时可能有其他线程进行了操作SegmentK,V s new SegmentK,V(lf, threshold, tab);// 自旋检查 u 位置的 Segment 是否为nullwhile ((seg (SegmentK,V)UNSAFE.getObjectVolatile(ss, u)) null) {// 使用CAS 赋值只会成功一次if (UNSAFE.compareAndSwapObject(ss, u, null, seg s))break;}}}return seg; }上面的源码分析了 ConcurrentHashMap 在 put 一个数据时的处理流程下面梳理下具体流程 计算要 put 的 key 的位置获取指定位置的 Segment。如果指定位置的 Segment 为空则初始化这个 Segment. 初始化 Segment 流程 检查计算得到的位置的 Segment 是否为null为 null 继续初始化使用 Segment[0] 的容量和负载因子创建一个 HashEntry 数组再次检查计算得到的指定位置的 Segment 是否为null使用创建的 HashEntry 数组初始化这个 Segment自旋判断计算得到的指定位置的 Segment 是否为null使用 CAS 在这个位置赋值为 SegmentSegment.put 插入 key,value 值。 上面探究了获取 Segment 段和初始化 Segment 段的操作。 最后一行的 Segment 的 put 方法还没有查看继续分析 final V put(K key, int hash, V value, boolean onlyIfAbsent) {// 获取 ReentrantLock 独占锁获取不到scanAndLockForPut 获取。HashEntryK,V node tryLock() ? null : scanAndLockForPut(key, hash, value);V oldValue;try {HashEntryK,V[] tab table;// 计算要put的数据位置int index (tab.length - 1) hash;// CAS 获取 index 坐标的值HashEntryK,V first entryAt(tab, index);for (HashEntryK,V e first;;) {if (e ! null) {// 检查是否 key 已经存在如果存在则遍历链表寻找位置找到后替换 valueK k;if ((k e.key) key ||(e.hash hash key.equals(k))) {oldValue e.value;if (!onlyIfAbsent) {e.value value;modCount;}break;}e e.next;}else {// first 有值没说明 index 位置已经有值了有冲突链表头插法。if (node ! null)node.setNext(first);elsenode new HashEntryK,V(hash, key, value, first);int c count 1;// 容量大于扩容阀值小于最大容量进行扩容if (c threshold tab.length MAXIMUM_CAPACITY)rehash(node);else// index 位置赋值 nodenode 可能是一个元素也可能是一个链表的表头setEntryAt(tab, index, node);modCount;count c;oldValue null;break;}}} finally {unlock();}return oldValue; }由于 Segment 继承了 ReentrantLock所以 Segment 内部可以很方便的获取锁put 流程就用到了这个功能。 tryLock() 获取锁获取不到使用 scanAndLockForPut 方法继续获取计算 put 的数据要放入的 index 位置然后获取这个位置上的 HashEntry遍历 put 新元素为什么要遍历因为这里获取的 HashEntry 可能是一个空元素也可能是链表已存在所以要区别对待。 如果这个位置上的 HashEntry 不存在 如果当前容量大于扩容阀值小于最大容量进行扩容直接头插法插入。 如果这个位置上的 HashEntry 存在 判断链表当前元素 Key 和 hash 值是否和要 put 的 key 和 hash 值一致。一致则替换值不一致获取链表下一个节点直到发现相同进行值替换或者链表表里完毕没有相同的。 如果当前容量大于扩容阀值小于最大容量进行扩容直接链表头插法插入 如果要插入的位置之前已经存在替换后返回旧值否则返回 null 这里面的第一步中的 scanAndLockForPut 操作这里没有介绍这个方法做的操作就是不断的自旋 tryLock() 获取锁。 当自旋次数大于指定次数时使用 lock() 阻塞获取锁。在自旋时顺表获取下 hash 位置的 HashEntry。 下面结合源码查看一下 private HashEntryK,V scanAndLockForPut(K key, int hash, V value) {HashEntryK,V first entryForHash(this, hash);HashEntryK,V e first;HashEntryK,V node null;int retries -1; // negative while locating node// 自旋获取锁while (!tryLock()) {HashEntryK,V f; // to recheck first belowif (retries 0) {if (e null) {if (node null) // speculatively create nodenode new HashEntryK,V(hash, key, value, null);retries 0;}else if (key.equals(e.key))retries 0;elsee e.next;}else if (retries MAX_SCAN_RETRIES) {// 自旋达到指定次数后阻塞等到只到获取到锁lock();break;}else if ((retries 1) 0 (f entryForHash(this, hash)) ! first) {e first f; // re-traverse if entry changedretries -1;}}return node; }rehash 扩容 ConcurrentHashMap 的扩容只会扩容到原来的两倍。老数组里的数据移动到新的数组时位置要么不变要么变为 index oldSize参数里的 node 会在扩容之后使用链表头插法插入到指定位置。 private void rehash(HashEntryK,V node) {HashEntryK,V[] oldTable table;// 老容量int oldCapacity oldTable.length;// 新容量扩大两倍int newCapacity oldCapacity 1;// 新的扩容阀值 threshold (int)(newCapacity * loadFactor);// 创建新的数组HashEntryK,V[] newTable (HashEntryK,V[]) new HashEntry[newCapacity];// 新的掩码默认2扩容后是4-1是3二进制就是11。int sizeMask newCapacity - 1;for (int i 0; i oldCapacity ; i) {// 遍历老数组HashEntryK,V e oldTable[i];if (e ! null) {HashEntryK,V next e.next;// 计算新的位置新的位置只可能是不便或者是老的位置老的容量。int idx e.hash sizeMask;if (next null) // Single node on list// 如果当前位置还不是链表只是一个元素直接赋值newTable[idx] e;else { // Reuse consecutive sequence at same slot// 如果是链表了HashEntryK,V lastRun e;int lastIdx idx;// 新的位置只可能是不便或者是老的位置老的容量。// 遍历结束后lastRun 后面的元素位置都是相同的for (HashEntryK,V last next; last ! null; last last.next) {int k last.hash sizeMask;if (k ! lastIdx) {lastIdx k;lastRun last;}}// lastRun 后面的元素位置都是相同的直接作为链表赋值到新位置。newTable[lastIdx] lastRun;// Clone remaining nodesfor (HashEntryK,V p e; p ! lastRun; p p.next) {// 遍历剩余元素头插法到指定 k 位置。V v p.value;int h p.hash;int k h sizeMask;HashEntryK,V n newTable[k];newTable[k] new HashEntryK,V(h, p.key, v, n);}}}}// 头插法插入新的节点int nodeIndex node.hash sizeMask; // add the new nodenode.setNext(newTable[nodeIndex]);newTable[nodeIndex] node;table newTable; }这里第一个 for 是为了寻找这样一个节点这个节点后面的所有 next 节点的新位置都是相同的然后把这个作为一个链表赋值到新位置。 第二个 for 循环是为了把剩余的元素通过头插法插入到指定位置链表。 get 操作 到这里就很简单了get 方法只需要两步即可 计算得到 key 的存放位置。遍历指定位置查找相同 key 的 value 值。 public V get(Object key) {SegmentK,V s; // manually integrate access methods to reduce overheadHashEntryK,V[] tab;int h hash(key);long u (((h segmentShift) segmentMask) SSHIFT) SBASE;// 计算得到 key 的存放位置if ((s (SegmentK,V)UNSAFE.getObjectVolatile(segments, u)) ! null (tab s.table) ! null) {for (HashEntryK,V e (HashEntryK,V) UNSAFE.getObjectVolatile(tab, ((long)(((tab.length - 1) h)) TSHIFT) TBASE);e ! null; e e.next) {// 如果是链表遍历查找到相同 key 的 value。K k;if ((k e.key) key || (e.hash h key.equals(k)))return e.value;}}return null; }ConcurrentHashMap 1.8 存储结构 可以发现 Java8 的 ConcurrentHashMap 相对于 Java7 来说变化比较大不再是之前的 Segment 数组 HashEntry 数组 链表而是 Node 数组 链表 / 红黑树。 当冲突链表达到一定长度时链表会转换成红黑树。 CAS 操作 JDK 1.8 的 ConcurrentHashMap 保证线程安全是依赖于 CAS 操作因此先来介绍一下这个 CASCompare-and-Swap/Exchange即比较并替换是一种实现并发常用到的技术。 CAS核心算法 执行函数CAS (VEN)V 表示准备要被更新的变量 内存的值E 表示我们提供的 期望的值 期望的原值N 表示新值 准备更新 V 的值 新值 算法思路 V是共享变量我们拿着自己准备的这个E去跟V去比较 如果 E V 说明当前没有其它线程在操作所以我们把 N 这个值 写入对象的 V 变量中如果 E ! V 说明我们准备的这个 E 已经过时了所以我们要重新准备一个最新的E 去跟V 比较 比较成功后才能更新 V 的值为 N 如果多个线程同时使用CAS操作一个变量的时候只有一个线程能够修改成功。 其余的线程提供的期望值已经与共享变量的值不一样了所以均会失败。 由于CAS操作属于乐观派它总是认为自己能够操作成功所以操作失败的线程将会再次发起操作而不是被OS挂起。 所以说即使 CAS操作没有使用同步锁其它线程也能够知道对共享变量的影响。 因为其它线程没有被挂起并且将会再次发起修改尝试所以无锁操作即CAS操作天生免疫死锁。 另外一点需要知道的是CAS是系统原语CAS操作是一条CPU的原子指令所以不会有线程安全问题。 注意 ABA问题 E 和 E‘ 对比相同是不能保证百分百保证其他线程没有在自己线程执行计算的过程里抢锁成功过有可能其他线程操作后新 E’ 值和旧 E 值一样 解决方案 在 E 对象里加个操作次数变量就行每次判断时对比两个E和操作次数就OK了因为 ABA 问题中就算 E 相同操作次数也绝不相同 另外CAS是靠硬件实现的从而在硬件层面提升效率。实现方式是基于硬件平台的汇编指令在intel的CPU中使用的是 cmpxchg 指令。 但是在多核CPU的情况下这个指令也不能保证原子性需要在前面加上 lock 指令。lock 指令可以保证一个 CPU 核心在操作期间独占一片内存区域。这个实现方式为总线锁和缓存锁。 在多核处理器的结构中CPU 核心并不能直接访问内存而是统一通过一条总线访问。 总线锁就是锁住这条总线使其他核心无法访问内存。这种方式代价太大了会导致其他核心停止工作。 而缓存锁并不锁定总线只是锁定某部分内存区域。当一个 CPU 核心将内存区域的数据读取到自己的缓存区后它会锁定缓存对应的内存区域。锁住期间其他核心无法操作这块内存区域。 初始化 initTable /*** Initializes table, using the size recorded in sizeCtl.*/ private final NodeK,V[] initTable() {NodeK,V[] tab; int sc;while ((tab table) null || tab.length 0) {//如果 sizeCtl 0 ,说明另外的线程执行CAS 成功正在进行初始化。if ((sc sizeCtl) 0)// 让出 CPU 使用权Thread.yield(); // lost initialization race; just spinelse if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {try {if ((tab table) null || tab.length 0) {int n (sc 0) ? sc : DEFAULT_CAPACITY;SuppressWarnings(unchecked)NodeK,V[] nt (NodeK,V[])new Node?,?[n];table tab nt;sc n - (n 2);}} finally {sizeCtl sc;}break;}}return tab; }从源码中可以发现 ConcurrentHashMap 的初始化是通过自旋和 CAS 操作完成的。里面需要注意的是变量 sizeCtl 它的值决定着当前的初始化状态。 -1 说明正在初始化-N 说明有N-1个线程正在进行扩容表示 table 初始化大小如果 table 没有初始化表示 table 容量如果 table 已经初始化 put 操作 直接过一遍 put 源码 public V put(K key, V value) {return putVal(key, value, false); }/** Implementation for put and putIfAbsent */ final V putVal(K key, V value, boolean onlyIfAbsent) {// key 和 value 不能为空if (key null || value null) throw new NullPointerException();int hash spread(key.hashCode());int binCount 0;for (NodeK,V[] tab table;;) {// f 目标位置元素NodeK,V f; int n, i, fh;// fh 后面存放目标位置的元素 hash 值if (tab null || (n tab.length) 0)// 数组桶为空初始化数组桶自旋CAS)tab initTable();else if ((f tabAt(tab, i (n - 1) hash)) null) {// 桶内为空CAS 放入不加锁成功了就直接 break 跳出if (casTabAt(tab, i, null,new NodeK,V(hash, key, value, null)))break; // no lock when adding to empty bin}else if ((fh f.hash) MOVED)tab helpTransfer(tab, f);else {V oldVal null;// 使用 synchronized 加锁加入节点synchronized (f) {if (tabAt(tab, i) f) {// 说明是链表if (fh 0) {binCount 1;// 循环加入新的或者覆盖节点for (NodeK,V e f;; binCount) {K ek;if (e.hash hash ((ek e.key) key ||(ek ! null key.equals(ek)))) {oldVal e.val;if (!onlyIfAbsent)e.val value;break;}NodeK,V pred e;if ((e e.next) null) {pred.next new NodeK,V(hash, key,value, null);break;}}}else if (f instanceof TreeBin) {// 红黑树NodeK,V p;binCount 2;if ((p ((TreeBinK,V)f).putTreeVal(hash, key,value)) ! null) {oldVal p.val;if (!onlyIfAbsent)p.val value;}}}}if (binCount ! 0) {if (binCount TREEIFY_THRESHOLD)treeifyBin(tab, i);if (oldVal ! null)return oldVal;break;}}}addCount(1L, binCount);return null; }过程概述 根据 key 计算出 hashcode判断是否需要进行初始化即为当前 key 定位出的 Node如果为空表示当前位置可以写入数据利用 CAS 尝试写入失败则自旋保证成功如果当前位置的 hashcode MOVED -1则需要进行扩容如果都不满足则利用 synchronized 锁写入数据如果数量大于 TREEIFY_THRESHOLD 则要转换为红黑树 get 操作 get 流程比较简单直接来吧 public V get(Object key) {NodeK,V[] tab; NodeK,V e, p; int n, eh; K ek;// key 所在的 hash 位置int h spread(key.hashCode());if ((tab table) ! null (n tab.length) 0 (e tabAt(tab, (n - 1) h)) ! null) {// 如果指定位置元素存在头结点hash值相同if ((eh e.hash) h) {if ((ek e.key) key || (ek ! null key.equals(ek)))// key hash 值相等key值相同直接返回元素 valuereturn e.val;}else if (eh 0)// 头结点hash值小于0说明正在扩容或者是红黑树find查找return (p e.find(h, key)) ! null ? p.val : null;while ((e e.next) ! null) {// 是链表遍历查找if (e.hash h ((ek e.key) key || (ek ! null key.equals(ek))))return e.val;}}return null; }总结一下 get 过程 根据 hash 值计算位置查找到指定位置如果头节点就是要找的直接返回它的 value如果头节点 hash 值小于 0 说明正在扩容或者是红黑树查找之如果是链表遍历查找之 总结 Java7 中 ConcurrentHashMap 使用的分段锁也就是每一个 Segment 上同时只有一个线程可以操作每一个 Segment 都是一个类似 HashMap 数组的结构它可以扩容它的冲突会转化为链表。但是 Segment 的个数一但初始化就不能改变。 Java8 中的 ConcurrentHashMap 使用的 Synchronized 锁加 CAS 的机制。结构也由 Java7 中的 Segment 数组 HashEntry 数组 链表 进化成了 Node 数组 链表 / 红黑树Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树在冲突小于一定数量时又退回链表。
文章转载自:
http://www.morning.mnqg.cn.gov.cn.mnqg.cn
http://www.morning.fxkgp.cn.gov.cn.fxkgp.cn
http://www.morning.plkrl.cn.gov.cn.plkrl.cn
http://www.morning.rnzwh.cn.gov.cn.rnzwh.cn
http://www.morning.xjkfb.cn.gov.cn.xjkfb.cn
http://www.morning.ssqwr.cn.gov.cn.ssqwr.cn
http://www.morning.yzmzp.cn.gov.cn.yzmzp.cn
http://www.morning.sxwfx.cn.gov.cn.sxwfx.cn
http://www.morning.mxhys.cn.gov.cn.mxhys.cn
http://www.morning.eshixi.com.gov.cn.eshixi.com
http://www.morning.shprz.cn.gov.cn.shprz.cn
http://www.morning.gwkjg.cn.gov.cn.gwkjg.cn
http://www.morning.vaqmq.cn.gov.cn.vaqmq.cn
http://www.morning.cpkcq.cn.gov.cn.cpkcq.cn
http://www.morning.klyzg.cn.gov.cn.klyzg.cn
http://www.morning.zpkfb.cn.gov.cn.zpkfb.cn
http://www.morning.ktntj.cn.gov.cn.ktntj.cn
http://www.morning.rpth.cn.gov.cn.rpth.cn
http://www.morning.hslgq.cn.gov.cn.hslgq.cn
http://www.morning.kongpie.com.gov.cn.kongpie.com
http://www.morning.jggr.cn.gov.cn.jggr.cn
http://www.morning.rlcqx.cn.gov.cn.rlcqx.cn
http://www.morning.jokesm.com.gov.cn.jokesm.com
http://www.morning.ptzf.cn.gov.cn.ptzf.cn
http://www.morning.sjwzz.cn.gov.cn.sjwzz.cn
http://www.morning.yngtl.cn.gov.cn.yngtl.cn
http://www.morning.qnxkm.cn.gov.cn.qnxkm.cn
http://www.morning.rmfw.cn.gov.cn.rmfw.cn
http://www.morning.rszbj.cn.gov.cn.rszbj.cn
http://www.morning.qlry.cn.gov.cn.qlry.cn
http://www.morning.xsctd.cn.gov.cn.xsctd.cn
http://www.morning.xkbdx.cn.gov.cn.xkbdx.cn
http://www.morning.yhplt.cn.gov.cn.yhplt.cn
http://www.morning.cndxl.cn.gov.cn.cndxl.cn
http://www.morning.sffwz.cn.gov.cn.sffwz.cn
http://www.morning.nktxr.cn.gov.cn.nktxr.cn
http://www.morning.swsrb.cn.gov.cn.swsrb.cn
http://www.morning.kndyz.cn.gov.cn.kndyz.cn
http://www.morning.hrtfz.cn.gov.cn.hrtfz.cn
http://www.morning.cfqyx.cn.gov.cn.cfqyx.cn
http://www.morning.bsrp.cn.gov.cn.bsrp.cn
http://www.morning.zstbc.cn.gov.cn.zstbc.cn
http://www.morning.nkddq.cn.gov.cn.nkddq.cn
http://www.morning.bktzr.cn.gov.cn.bktzr.cn
http://www.morning.qnzld.cn.gov.cn.qnzld.cn
http://www.morning.pdbgm.cn.gov.cn.pdbgm.cn
http://www.morning.ltywr.cn.gov.cn.ltywr.cn
http://www.morning.wwxg.cn.gov.cn.wwxg.cn
http://www.morning.mxhgy.cn.gov.cn.mxhgy.cn
http://www.morning.jqzns.cn.gov.cn.jqzns.cn
http://www.morning.xckrj.cn.gov.cn.xckrj.cn
http://www.morning.nbybb.cn.gov.cn.nbybb.cn
http://www.morning.jjhng.cn.gov.cn.jjhng.cn
http://www.morning.kycwt.cn.gov.cn.kycwt.cn
http://www.morning.qkgwz.cn.gov.cn.qkgwz.cn
http://www.morning.dnmgr.cn.gov.cn.dnmgr.cn
http://www.morning.nnrqg.cn.gov.cn.nnrqg.cn
http://www.morning.sthp.cn.gov.cn.sthp.cn
http://www.morning.xhddb.cn.gov.cn.xhddb.cn
http://www.morning.qkqpy.cn.gov.cn.qkqpy.cn
http://www.morning.bphqd.cn.gov.cn.bphqd.cn
http://www.morning.qhkx.cn.gov.cn.qhkx.cn
http://www.morning.wzyfk.cn.gov.cn.wzyfk.cn
http://www.morning.stbfy.cn.gov.cn.stbfy.cn
http://www.morning.qbfs.cn.gov.cn.qbfs.cn
http://www.morning.sypby.cn.gov.cn.sypby.cn
http://www.morning.joinyun.com.gov.cn.joinyun.com
http://www.morning.fddfn.cn.gov.cn.fddfn.cn
http://www.morning.tsgxz.cn.gov.cn.tsgxz.cn
http://www.morning.fqtzn.cn.gov.cn.fqtzn.cn
http://www.morning.rnngz.cn.gov.cn.rnngz.cn
http://www.morning.mbprq.cn.gov.cn.mbprq.cn
http://www.morning.kqpsj.cn.gov.cn.kqpsj.cn
http://www.morning.jbxfm.cn.gov.cn.jbxfm.cn
http://www.morning.psxxp.cn.gov.cn.psxxp.cn
http://www.morning.rzysq.cn.gov.cn.rzysq.cn
http://www.morning.rswfj.cn.gov.cn.rswfj.cn
http://www.morning.jzfrl.cn.gov.cn.jzfrl.cn
http://www.morning.yysqz.cn.gov.cn.yysqz.cn
http://www.morning.ytbr.cn.gov.cn.ytbr.cn
http://www.tj-hxxt.cn/news/238445.html

相关文章:

  • 博宇娱乐网站建设在线h5免费制作网站
  • 新昌建设局网站wordpress 网站锁
  • 微网站是不是就是手机网站wordpress的alt属性插件
  • 库尔勒 网站建设本地电脑如何做网站服务器
  • 骏驰网站建设广州做app软件开发的公司
  • 东营网站建设公司 网络易旅游网站项目策划书
  • 无锡建设局评职称网站知道网站域名怎么联系
  • 免费送衣服在哪个网站做佛山网站制作好处
  • 通过平台建网站免费网站怎么注册
  • php网站建设入门教程爱生活辽宁移动app
  • 高明建网站服务中卫网站建设
  • wordpress建站 百度网盘表示商业网站的域名
  • 微信网站案例wordpress 商业版权
  • 有没有做淘宝的网站吗it运维服务管理体系
  • 网站建设考级wordpress炫酷插件
  • 棋牌网站建设源码一键生成vi设计
  • 网站导航插件静安青岛网站建设
  • 建立网站的过程移动端网站设计欣赏
  • 开网站做网站赚钱吗昆明网站建设c3sales
  • 谷歌排名网站优化dede网站前台没有图片
  • 国家电网网站制作官方手表网站
  • 网站图片像素亚成成品网站源码
  • 自己做的小网站如何发布建工集团两学一做网站
  • 免费制作二维码的网站洛阳网站建设的公司哪家好
  • wordpress 全站404我做微信淘宝客网站
  • 好玩的电脑网页游戏苏州seo怎么做
  • 上海网站建设网站优化app漳州网站建设优化
  • 做网站容易还是app容易网站制作服务公司
  • 网站设计报价单网站建设能用手机制作吗
  • 浏览器什么网站都能打开的商标注册费用一般是多少钱