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

上海营销型网站建设需要推销自己做网站的公司

上海营销型网站建设,需要推销自己做网站的公司,重庆快速排名优化,网站做整站做优化没有CAS之前实现线程安全 多线程环境不使用原子类保证线程安全#xff08;基本数据类型#xff09; public class T3 {volatile int number 0;//读取public int getNumber(){return number;}//写入加锁保证原子性public synchronized void setNumber(){number;} }多线程环… 没有CAS之前实现线程安全 多线程环境不使用原子类保证线程安全基本数据类型 public class T3 {volatile int number 0;//读取public int getNumber(){return number;}//写入加锁保证原子性public synchronized void setNumber(){number;} }多线程环境 使用原子类保证线程安全基本数据类型 public class T3 {AtomicInteger atomicInteger new AtomicInteger();public int getAtomicInteger(){return atomicInteger.get();}public void setAtomicInteger(){atomicInteger.getAndIncrement();} }什么是CAS compare and swap的缩写中文翻译成比较并交换,实现并发算法时常用到的一种技术。它包含三个操作数——内存位置、预期原值及更新值。 执行CAS操作的时候将内存位置的值与预期原值比较如果相匹配那么处理器会自动将该位置值更新为新值如果不匹配处理器不做任何操作多个线程同时执行CAS操作只有一个会成功。 CAS CompareAndSwap 有3个操作数位置内存值V旧的预期值A要修改的更新值B。当且仅当旧的预期值A和内存值V相同时将内存值V修改为B否则什么都不做或重来。 硬件级别保证 CAS是JDK提供的非阻塞原子性操作它通过硬件保证了比较-更新的原子性。 它是非阻塞的且自身原子性也就是说这玩意效率更高且通过硬件保证说明这玩意更可靠。 CAS是一条CPU的原子指令cmpxchg指令不会造成所谓的数据不一致问题Unsafe提供的CAS方法如compareAndSwapXXX底层实现即为CPU指令cmpxchg。 执行cmpxchg指令的时候会判断当前系统是否为多核系统如果是就给总线加锁只有一个线程会对总线加锁成功加锁成功之后会执行cas操作也就是说CAS的原子性实际上是CPU实现的 其实在这一点上还是有排他锁的只是比起用synchronized 这里的排他时间要短的多 所以在多线程情况下性能会比较好。 CASDemo代码 public class CASDemo {public static void main(String[] args) throws InterruptedException{AtomicInteger atomicInteger new AtomicInteger(5);System.out.println(atomicInteger.compareAndSet(5, 2020)\tatomicInteger.get());System.out.println(atomicInteger.compareAndSet(5, 1024)\tatomicInteger.get());} }源码分析compareAndSet(int expect,int update) compareAndSet()方法的源代码 上面三个方法都是类似的主要对4个参数做一下说明。 var1表示要操作的对象 var2表示要操作对象中属性地址的偏移量 var4表示需要修改数据的期望的值 var5/var6表示需要修改为的新值 引出来一个问题UnSafe类是什么 CAS底层原理如果知道谈谈你对UnSafe的理解 UnSafe 是CAS的核心类由于Java方法无法直接访问底层系统需要通过本地native方法来访问Unsafe相当于一个后门基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中其内部方法操作可以像C的指针一样直接操作内存因为Java中CAS操作的执行依赖于Unsafe类的方法。 注意Unsafe类中的所有方法都是native修饰的也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务 变量valueOffset表示该变量值在内存中的偏移地址因为Unsafe就是根据内存偏移地址获取数据的。 变量value用volatile修饰保证了多线程之间的内存可见性。 我们知道i线程不安全的那atomicInteger.getAndIncrement() CAS的全称为Compare-And-Swap它是一条CPU并发原语。 它的功能是判断内存某个位置的值是否为预期值如果是则更改为新的值这个过程是原子的。 AtomicInteger 类主要利用 CAS (compare and swap) volatile 和 native 方法来保证原子操作从而避免 synchronized 的高开销执行效率大为提升。 CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能通过它实现了原子操作。再次强调由于CAS是一种系统原语原语属于操作系统用语范畴是由若干条指令组成的用于完成某个功能的一个过程并且原语的执行必须是连续的在执行过程中不允许被中断也就是说CAS是一条CPU的原子指令不会造成所谓的数据不一致问题。 源码分析 OpenJDK源码里面查看下Unsafe.java 假设线程A和线程B两个线程同时执行getAndAddInt操作分别跑在不同CPU上 1 AtomicInteger里面的value原始值为3即主内存中AtomicInteger的value为3根据JMM模型线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存。 2 线程A通过getIntVolatile(var1, var2)拿到value值3这时线程A被挂起。 3 线程B也通过getIntVolatile(var1, var2)方法获取到value值3此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存值也为3成功修改内存值为4线程B打完收工一切OK。 4 这时线程A恢复执行compareAndSwapInt方法比较发现自己手里的值数字3和主内存的值数字4不一致说明该值已经被其它线程抢先一步修改过了那A线程本次修改失败只能重新读取重新来一遍了。 5 线程A重新获取value值因为变量value被volatile修饰所以其它线程对它的修改线程A总是能够看到线程A继续执行compareAndSwapInt进行比较替换直到成功。 总结 你只需要记住CAS是靠硬件实现的从而在硬件层面提升效率最底层还是交给硬件来保证原子性和可见性 实现方式是基于硬件平台的汇编指令在intel的CPU中(X86机器上)使用的是汇编指令cmpxchg指令。 核心思想就是比较要更新变量的值V和预期值Ecompare相等才会将V的值设为新值Nswap如果不相等自旋再来。 原子引用 Java中提供了几类原子操作类通过自旋CAS来解决线程不安全问题。 基本类型原子类 -AtomicLong、AtomicInteger、AtomicBoolean 这些类实现了–delta的原子操作。 常用API简介 public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值并设置新的值 public final int getAndIncrement()//获取当前的值并自增 public final int getAndDecrement() //获取当前的值并自减 public final int getAndAdd(int delta) //获取当前的值并加上预期的值 boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值则以原子方式将该值设置为输入值update class MyNumber {Getterprivate AtomicInteger atomicInteger new AtomicInteger();public void addPlusPlus(){atomicInteger.incrementAndGet();} }public class AtomicIntegerDemo {public static void main(String[] args) throws InterruptedException{MyNumber myNumber new MyNumber();CountDownLatch countDownLatch new CountDownLatch(100);for (int i 1; i 100; i) {new Thread(() - {try{for (int j 1; j 5000; j){myNumber.addPlusPlus();}}finally {countDownLatch.countDown();}},String.valueOf(i)).start();}countDownLatch.await();System.out.println(myNumber.getAtomicInteger().get());} }数组类型原子类- AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 这个就是数组类型和单独的对象操作基本一致只不过在设置的时候需要填入下标罢了。 public class AtomicIntegerArrayDemo {public static void main(String[] args){AtomicIntegerArray atomicIntegerArray new AtomicIntegerArray(new int[5]);//AtomicIntegerArray atomicIntegerArray new AtomicIntegerArray(5);//AtomicIntegerArray atomicIntegerArray new AtomicIntegerArray(new int[]{1,2,3,4,5});for (int i 0; i atomicIntegerArray.length(); i) {System.out.println(atomicIntegerArray.get(i));}System.out.println();System.out.println();System.out.println();int tmpInt 0;tmpInt atomicIntegerArray.getAndSet(0,1122);System.out.println(tmpInt\tatomicIntegerArray.get(0));atomicIntegerArray.getAndIncrement(1);atomicIntegerArray.getAndIncrement(1);tmpInt atomicIntegerArray.getAndIncrement(1);System.out.println(tmpInt\tatomicIntegerArray.get(1));} }引用类型原子类- AtomicReference、AtomicStampedReference、AtomicMarkableReference AtomicReference Getter ToString AllArgsConstructor class User {String userName;int age; }public class AtomicReferenceDemo {public static void main(String[] args){User z3 new User(z3,24);User li4 new User(li4,26);AtomicReferenceUser atomicReferenceUser new AtomicReference();atomicReferenceUser.set(z3);System.out.println(atomicReferenceUser.compareAndSet(z3,li4)\tatomicReferenceUser.get().toString());System.out.println(atomicReferenceUser.compareAndSet(z3,li4)\tatomicReferenceUser.get().toString());} }AtomicStampedReference 携带版本号的引用类型原子类可以解决ABA问题 public class ABADemo {static AtomicInteger atomicInteger new AtomicInteger(100);static AtomicStampedReference atomicStampedReference new AtomicStampedReference(100,1);public static void main(String[] args){abaProblem();abaResolve();}public static void abaResolve(){new Thread(() - {int stamp atomicStampedReference.getStamp();System.out.println(t3 ----第1次stamp stamp);try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }atomicStampedReference.compareAndSet(100,101,stamp,stamp1);System.out.println(t3 ----第2次stamp atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()1);System.out.println(t3 ----第3次stamp atomicStampedReference.getStamp());},t3).start();new Thread(() - {int stamp atomicStampedReference.getStamp();System.out.println(t4 ----第1次stamp stamp);//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }boolean result atomicStampedReference.compareAndSet(100, 20210308, stamp, stamp 1);System.out.println(Thread.currentThread().getName()\tresult\tatomicStampedReference.getReference());},t4).start();}public static void abaProblem(){new Thread(() - {atomicInteger.compareAndSet(100,101);atomicInteger.compareAndSet(101,100);},t1).start();try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() - {atomicInteger.compareAndSet(100,20210308);System.out.println(atomicInteger.get());},t2).start();} }AtomicMarkableReference 原子更新带有标记位的引用类型对象 public class ABADemo {static AtomicInteger atomicInteger new AtomicInteger(100);static AtomicStampedReferenceInteger stampedReference new AtomicStampedReference(100,1);static AtomicMarkableReferenceInteger markableReference new AtomicMarkableReference(100,false);public static void main(String[] args){new Thread(() - {atomicInteger.compareAndSet(100,101);atomicInteger.compareAndSet(101,100);System.out.println(Thread.currentThread().getName()\tupdate ok);},t1).start();new Thread(() - {//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }atomicInteger.compareAndSet(100,2020);},t2).start();//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(atomicInteger.get());System.out.println();System.out.println();System.out.println();System.out.println(以下是ABA问题的解决,让我们知道引用变量中途被更改了几次);new Thread(() - {System.out.println(Thread.currentThread().getName()\t 1次版本号stampedReference.getStamp());//故意暂停200毫秒让后面的t4线程拿到和t3一样的版本号try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()1);System.out.println(Thread.currentThread().getName()\t 2次版本号stampedReference.getStamp());stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()1);System.out.println(Thread.currentThread().getName()\t 3次版本号stampedReference.getStamp());},t3).start();new Thread(() - {int stamp stampedReference.getStamp();System.out.println(Thread.currentThread().getName()\t 1次版本号stamp);//暂停2秒钟,让t3先完成ABA操作了看看自己还能否修改try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }boolean b stampedReference.compareAndSet(100, 2020, stamp, stamp 1);System.out.println(Thread.currentThread().getName()\t2次版本号stampedReference.getStamp()\tstampedReference.getReference());},t4).start();System.out.println();System.out.println();System.out.println();System.out.println(AtomicMarkableReference不关心引用变量更改过几次只关心是否更改过);new Thread(() - {boolean marked markableReference.isMarked();System.out.println(Thread.currentThread().getName()\t 1次版本号marked);try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }markableReference.compareAndSet(100,101,marked,!marked);System.out.println(Thread.currentThread().getName()\t 2次版本号markableReference.isMarked());markableReference.compareAndSet(101,100,markableReference.isMarked(),!markableReference.isMarked());System.out.println(Thread.currentThread().getName()\t 3次版本号markableReference.isMarked());},t5).start();new Thread(() - {boolean marked markableReference.isMarked();System.out.println(Thread.currentThread().getName()\t 1次版本号marked);//暂停几秒钟线程try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }markableReference.compareAndSet(100,2020,marked,!marked);System.out.println(Thread.currentThread().getName()\tmarkableReference.getReference()\tmarkableReference.isMarked());},t6).start();} }原子操作增强类原理深度解析 DoubleAccumulatorDoubleAdderLongAccumulatorLongAdder 阿里要命题目 常用API LongAdder只能用来计算加法且从零开始计算 LongAccumulator提供了自定义的函数操作 Demo public class LongAdderAPIDemo {public static void main(String[] args){LongAdder longAdder new LongAdder();longAdder.increment();longAdder.increment();longAdder.increment();System.out.println(longAdder.longValue());LongAccumulator longAccumulator new LongAccumulator((x,y) - x * y,2);longAccumulator.accumulate(1);longAccumulator.accumulate(2);longAccumulator.accumulate(3);System.out.println(longAccumulator.longValue());} }LongAdder高性能对比Code演示 class ClickNumberNet {int number 0;public synchronized void clickBySync(){number;}AtomicLong atomicLong new AtomicLong(0);public void clickByAtomicLong(){atomicLong.incrementAndGet();}LongAdder longAdder new LongAdder();public void clickByLongAdder(){longAdder.increment();}LongAccumulator longAccumulator new LongAccumulator((x,y) - x y,0);public void clickByLongAccumulator(){longAccumulator.accumulate(1);} }public class LongAdderDemo2 {public static void main(String[] args) throws InterruptedException{ClickNumberNet clickNumberNet new ClickNumberNet();long startTime;long endTime;CountDownLatch countDownLatch new CountDownLatch(50);CountDownLatch countDownLatch2 new CountDownLatch(50);CountDownLatch countDownLatch3 new CountDownLatch(50);CountDownLatch countDownLatch4 new CountDownLatch(50);startTime System.currentTimeMillis();for (int i 1; i 50; i) {new Thread(() - {try{for (int j 1; j 100 * 10000; j) {clickNumberNet.clickBySync();}}finally {countDownLatch.countDown();}},String.valueOf(i)).start();}countDownLatch.await();endTime System.currentTimeMillis();System.out.println(----costTime: (endTime - startTime) 毫秒\t clickBySync result: clickNumberNet.number);startTime System.currentTimeMillis();for (int i 1; i 50; i) {new Thread(() - {try{for (int j 1; j 100 * 10000; j) {clickNumberNet.clickByAtomicLong();}}finally {countDownLatch2.countDown();}},String.valueOf(i)).start();}countDownLatch2.await();endTime System.currentTimeMillis();System.out.println(----costTime: (endTime - startTime) 毫秒\t clickByAtomicLong result: clickNumberNet.atomicLong);startTime System.currentTimeMillis();for (int i 1; i 50; i) {new Thread(() - {try{for (int j 1; j 100 * 10000; j) {clickNumberNet.clickByLongAdder();}}finally {countDownLatch3.countDown();}},String.valueOf(i)).start();}countDownLatch3.await();endTime System.currentTimeMillis();System.out.println(----costTime: (endTime - startTime) 毫秒\t clickByLongAdder result: clickNumberNet.longAdder.sum());startTime System.currentTimeMillis();for (int i 1; i 50; i) {new Thread(() - {try{for (int j 1; j 100 * 10000; j) {clickNumberNet.clickByLongAccumulator();}}finally {countDownLatch4.countDown();}},String.valueOf(i)).start();}countDownLatch4.await();endTime System.currentTimeMillis();System.out.println(----costTime: (endTime - startTime) 毫秒\t clickByLongAccumulator result: clickNumberNet.longAccumulator.longValue());} }源码、原理分析 架构 LongAdder是Striped64的子类 原理(LongAdder为什么这么快) LongAdder是Striped64的子类,Striped64有几个比较重要的成员函数 /** Number of CPUS, to place bound on table size CPU数量即cells数组的最大长度 */ static final int NCPU Runtime.getRuntime().availableProcessors();/*** Table of cells. When non-null, size is a power of 2. cells数组为2的幂2,4,8,16.....方便以后位运算*/ transient volatile Cell[] cells;/**基础value值当并发较低时只累加该值主要用于没有竞争的情况通过CAS更新。* Base value, used mainly when there is no contention, but also as* a fallback during table initialization races. Updated via CAS.*/ transient volatile long base;/**创建或者扩容Cells数组时使用的自旋锁变量调整单元格大小扩容创建单元格时使用的锁。* Spinlock (locked via CAS) used when resizing and/or creating Cells. */ transient volatile int cellsBusy;Cell - 是 java.util.concurrent.atomic 下 Striped64 的一个内部类 LongAdder为什么这么快 LongAdder的基本思路就是分散热点将value值分散到一个Cell数组中不同线程会命中到数组的不同槽中各个线程只对自己槽中的那个值进行CAS操作这样热点就被分散了冲突的概率就小很多。如果要获取真正的long值只要将各个槽中的变量值累加返回。 内部有一个base变量一个Cell[]数组。sum()会将所有Cell数组中的value和base累加作为返回值核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去 从而降级更新热点。 LongAdder在无竞争的情况跟AtomicLong一样对同一个base进行操作当出现竞争关系时则是采用化整为零的做法从空间换时间用一个数组cells将一个value拆分进这个数组cells。多个线程需要同时对value进行操作时候可以对线程id进行hash得到hash值再根据hash值映射到这个数组cells的某个下标再对该下标所对应的值进行自增操作。当所有线程操作完毕将数组cells的所有值和无竞争值base都加起来作为最终结果。 总结 AtomicLong 原理 CAS自旋incrementAndGet 场景 低并发下的全局计算AtomicLong能保证并发情况下计数的准确性其内部通过CAS来解决并发安全性的问题。 缺陷 高并发后性能急剧下降AtomicLong的自旋会成为瓶颈 N个线程CAS操作修改线程的值每次只有一个成功过其它N - 1失败失败的不停的自旋直到成功这样大量失败自旋的情况一下子cpu就打高了。 LongAdder 原理 CASBaseCell数组分散空间换时间并分散了热点数据 场景 高并发下的全局计算 缺陷 sum求和后还有计算线程修改结果的话最后结果不够准确 自旋锁借鉴CAS思想 自旋锁spinlock 是指尝试获取锁的线程不会立即阻塞而是采用循环的方式去尝试获取锁 当线程发现锁被占用时会不断循环判断锁的状态直到获取。这样的好处是减少线程上下文切换的消耗缺点是循环会消耗CPU /*** 题目实现一个自旋锁* 自旋锁好处循环比较获取没有类似wait的阻塞。** 通过CAS操作完成自旋锁A线程先进来调用myLock方法自己持有锁5秒钟B随后进来后发现* 当前有线程持有锁不是null所以只能通过自旋等待直到A释放锁后B随后抢到。*/ public class SpinLockDemo {AtomicReferenceThread atomicReference new AtomicReference();public void myLock(){Thread thread Thread.currentThread();System.out.println(Thread.currentThread().getName()\t come in);while(!atomicReference.compareAndSet(null,thread)){}}public void myUnLock(){Thread thread Thread.currentThread();atomicReference.compareAndSet(thread,null);System.out.println(Thread.currentThread().getName()\t myUnLock over);}public static void main(String[] args){SpinLockDemo spinLockDemo new SpinLockDemo();new Thread(() - {spinLockDemo.myLock();try { TimeUnit.SECONDS.sleep( 5 ); } catch (InterruptedException e) { e.printStackTrace(); }spinLockDemo.myUnLock();},A).start();//暂停一会儿线程保证A线程先于B线程启动并完成try { TimeUnit.SECONDS.sleep( 1 ); } catch (InterruptedException e) { e.printStackTrace(); }new Thread(() - {spinLockDemo.myLock();spinLockDemo.myUnLock();},B).start();} }CAS缺点 循环时间长开销很大 我们可以看到getAndAddInt方法执行时有个do while 如果CAS失败会一直进行尝试。如果CAS长时间一直不成功可能会给CPU带来很大的开销。 引出来ABA问题 ABA问题怎么产生的 CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换那么在这个时间差类会导致数据的变化。 比如说一个线程one从内存位置V中取出A这时候另一个线程two也从内存中取出A并且线程two进行了一些操作将值变成了B 然后线程two又将V位置的数据变成A这时候线程one进行CAS操作发现内存中仍然是A然后线程one操作成功。 尽管线程one的CAS操作成功但是不代表这个过程就是没有问题的。 解决方案-版本号时间戳原子引用 AtomicStampedReference AtomicStampedReference在构建的时候需要一个类似于版本号的int类型变量stamped每一次针对共享数据的变化都会导致该 stamped 的变化stamped 需要应用程序自身去负责AtomicStampedReference并不提供一般使用时间戳作为版本号因此就可以避免ABA问题的出现AtomicStampedReference的使用也是极其简单的创建时我们不仅需要指定初始值还需要设定stamped的初始值在AtomicStampedReference的内部会将这两个变量封装成Pair对象代码如下所示。 public class ABADemo {static AtomicInteger atomicInteger new AtomicInteger(100);static AtomicStampedReference atomicStampedReference new AtomicStampedReference(100,1);public static void main(String[] args){new Thread(() - {atomicInteger.compareAndSet(100,101);atomicInteger.compareAndSet(101,100);},t1).start();new Thread(() - {//暂停一会儿线程try { Thread.sleep( 500 ); } catch (InterruptedException e) { e.printStackTrace(); }; System.out.println(atomicInteger.compareAndSet(100, 2019)\tatomicInteger.get());},t2).start();//暂停一会儿线程,main彻底等待上面的ABA出现演示完成。try { Thread.sleep( 2000 ); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println(以下是ABA问题的解决);new Thread(() - {int stamp atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName()\t 首次版本号:stamp);//1//暂停一会儿线程,try { Thread.sleep( 1000 ); } catch (InterruptedException e) { e.printStackTrace(); }atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()1);System.out.println(Thread.currentThread().getName()\t 2次版本号:atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()1);System.out.println(Thread.currentThread().getName()\t 3次版本号:atomicStampedReference.getStamp());},t3).start();new Thread(() - {int stamp atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName()\t 首次版本号:stamp);//1//暂停一会儿线程获得初始值100和初始版本号1故意暂停3秒钟让t3线程完成一次ABA操作产生问题try { Thread.sleep( 3000 ); } catch (InterruptedException e) { e.printStackTrace(); }boolean result atomicStampedReference.compareAndSet(100,2019,stamp,stamp1);System.out.println(Thread.currentThread().getName()\tresult\tatomicStampedReference.getReference());},t4).start();} }
http://www.hkea.cn/news/14466278/

相关文章:

  • 贸易做网站怎么找网站
  • 网站建设与维护是什么内容?做网站用突发性实例可以吗
  • 北京网站建设公司有哪些58同城遵义
  • 做订票网站设计要多久聊天网站开发
  • 深圳做电子工厂的网站品牌网站设计哪家好
  • 淘客怎么用网站做门户网站app
  • wordpress post编辑莆田网站关键词优化
  • 网站设计公司皆选奇点网络银座网上商城
  • 深圳外贸网站外贸网站建设apmserv安装wordpress
  • 资阳视频网站建设营销型门户网站建设
  • 哪个网站上做自媒体最好通化市城乡建设局网站
  • 询价网站哪个好南山电商网站建设
  • 单站点网站wordpress播放代码
  • 怎样免费建设个人网站免费搭建购物网站
  • 西安网站制作公司怎么选tp框架做网站的优点
  • 做代账的网站江门网站开发公司
  • 网站怎么做响应式如何做请求队列防止网站高并发
  • 莱阳网站建设公司上市公司的信息网站
  • 太原电商网站设计首都产业建设集团网站
  • 网站建设需要写语句吗网站上图怎么用ps做
  • 做p2p网站案例typecho客户端wordpress
  • 电影网站虚拟主机和vps为什么要建设o2o网站
  • 最火网站排名网络推广软件全邀zjkwlgs
  • 黑龙江做网站做博客和做网站
  • 这几年做那个网站致富网站是怎么被挂马
  • 一个空间怎么放多个网站wordpress购买下载
  • 杭州网站建设seo优化建设摩托车官方旗舰店
  • 景点介绍网站开发设计西安网络推广哪家好
  • 大沥网站制作wap网
  • 网站建站网站建设小x导航正品