中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁(yè) > news >正文

滁州做網(wǎng)站電話號(hào)碼/seo推廣服務(wù)哪家好

滁州做網(wǎng)站電話號(hào)碼,seo推廣服務(wù)哪家好,蘇州房產(chǎn)網(wǎng),網(wǎng)頁(yè)設(shè)計(jì)基礎(chǔ)知識(shí)總結(jié)文章目錄并發(fā)編程的三大特性1、原子性什么是并發(fā)編程的原子性?保證并發(fā)編程的原子性synchronizedCASLock鎖ThreadLocal2、可見性什么是可見性?解決可見性的方式volatilesynchronizedLockfinal3、有序性什么是有序性?as-if-serialhappens-beforevolatile并發(fā)編程的…

文章目錄

  • 并發(fā)編程的三大特性
  • 1、原子性
    • 什么是并發(fā)編程的原子性?
    • 保證并發(fā)編程的原子性
      • synchronized
      • CAS
      • Lock鎖
      • ThreadLocal
  • 2、可見性
    • 什么是可見性?
    • 解決可見性的方式
      • volatile
      • synchronized
      • Lock
      • final
  • 3、有序性
      • 什么是有序性?
      • as-if-serial
      • happens-before
      • volatile

并發(fā)編程的三大特性

原子性、可見性、有序性。

1、原子性

什么是并發(fā)編程的原子性?

JMM(Java Memory Model)。不同的硬件和不同的操作系統(tǒng)在內(nèi)存上的操作有一定差異的。 Java為了解決相同代碼在不同操作系統(tǒng)上出現(xiàn)的各種問題,用JMM屏蔽掉各種硬件和操作系統(tǒng)帶來的差異。
讓Java的并發(fā)編程可以做到跨平臺(tái)。 JMM規(guī)定所有變量都會(huì)存儲(chǔ)在主內(nèi)存中,在操作的時(shí)候,需要從主內(nèi)存中復(fù)制一份到線程內(nèi)存(CPU內(nèi)存),在線程內(nèi)部做計(jì)算。然后再寫回主內(nèi)存中(不一定)。
原子性的定義:原子性指一個(gè)操作是不可分割的,不可中斷的,一個(gè)線程在執(zhí)行時(shí),另一個(gè)線程不會(huì)影響到他。
并發(fā)編程的原子性用代碼闡述:

    private static int count;public static void increment() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}count++;}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}

當(dāng)前程序:多線程操作共享數(shù)據(jù)時(shí),預(yù)期的結(jié)果,與最終的結(jié)果不符。
原子性:多線程操作臨界資源,預(yù)期的結(jié)果與最終結(jié)果一致。
通過對(duì)這個(gè)程序的分析,可以查看出,++的操作,一共分為了三部,首先是線程從主內(nèi)存拿到數(shù)據(jù),保存到CPU的寄存器中,然后在寄存器中進(jìn)行+1操作,最終將結(jié)果寫回到主內(nèi)存當(dāng)中。

保證并發(fā)編程的原子性

synchronized

因?yàn)?#43;+操作可以從指令中查看到
i++操作可以在方法上追加synchronized關(guān)鍵字或者采用同步代碼塊的形式來保證原子性。
synchronized可以讓避免多線程同時(shí)操作臨街資源,同一時(shí)間點(diǎn),只會(huì)有一個(gè)線程正在操作臨界資源。
在這里插入圖片描述

CAS

compare and swap也就是比較和交換,他是一條CPU的并發(fā)原語(yǔ)。
他在替換內(nèi)存的某個(gè)位置的值時(shí),首先查看內(nèi)存中的值與預(yù)期值是否一致,如果一致,執(zhí)行替換操作。這個(gè)操作是一個(gè)原子性操作。
Java中基于Unsafe的類提供了對(duì)CAS的操作的方法,JVM會(huì)幫助我們將方法實(shí)現(xiàn)CAS匯編指令。 但是要清楚CAS只是比較和交換,在獲取原值的這個(gè)操作上,需要你自己實(shí)現(xiàn)。

    private static AtomicInteger count = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {count.incrementAndGet();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {count.incrementAndGet();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}

Doug Lea在CAS的基礎(chǔ)上幫助我們實(shí)現(xiàn)了一些原子類,其中就包括現(xiàn)在看到的AtomicInteger,還有其他很多原子類。
CAS的缺點(diǎn):CAS只能保證對(duì)一個(gè)變量的操作是原子性的,無法實(shí)現(xiàn)對(duì)多行代碼實(shí)現(xiàn)原子性。

CAS帶來的問題

  • ABA問題:問題如下,可以引入版本號(hào)的方式,來解決ABA的問題。Java中提供了一個(gè)類在CAS時(shí),針對(duì)各個(gè)版本追加版本號(hào)的操作。 AtomicStampeReference在CAS時(shí),不但會(huì)判斷原值,還會(huì)比較版本信息。
        public static void main(String[] args) {AtomicStampedReference<String> reference = new AtomicStampedReference<>("AAA", 1);String oldValue = reference.getReference();int oldVersion = reference.getStamp();boolean b = reference.compareAndSet(oldValue, "B", oldVersion, oldVersion + 1);System.out.println("修改1版本的:" + b);boolean c = reference.compareAndSet("B", "C", 1, 1 + 1);System.out.println("修改2版本的:" + c);}
    
  • 自旋時(shí)間過長(zhǎng)問題
    • 可以指定CAS一共循環(huán)多少次,如果超過這個(gè)次數(shù),直接失敗/或者掛起線程。(自旋鎖、 自適應(yīng)自旋鎖)
    • 可以在CAS一次失敗后,將這個(gè)操作暫存起來,后面需要獲取結(jié)果時(shí),將暫存的操作全部執(zhí)行,再返回最后的結(jié)果。

ABA問題:在這里插入圖片描述

  • 線程1從內(nèi)存位置V中取出A
  • 線程2從內(nèi)存位置V中取出A
  • 線程2進(jìn)行了寫操作,將B寫入內(nèi)存位置V
  • 線程2將A再次寫入內(nèi)存位置V
  • 線程1進(jìn)行CAS操作,發(fā)現(xiàn)V中仍然是A,交換成功
    盡管線程1的CAS操作成功,但線程1并不知道內(nèi)存位置V的數(shù)據(jù)發(fā)生過改變。

Lock鎖

Lock鎖是在JDK1.5由Doug Lea研發(fā)的,他的性能相比synchronized在JDK1.5的時(shí)期,性能好了很很多,但是在JDK1.6對(duì)synchronized優(yōu)化之后,性能相差不大,但是如果涉及并發(fā)比較多時(shí),推薦ReentrantLock鎖,性能會(huì)更好。

	private static int count;private static ReentrantLock lock = new ReentrantLock();public static void increment() {lock.lock();try {count++;try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {increment();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}

ReentrantLock可以直接對(duì)比synchronized,在功能上來說,都是鎖。但是ReentrantLock的功能性相比synchronized更豐富。
ReentrantLock底層是基于AQS實(shí)現(xiàn)的,有一個(gè)基于CAS維護(hù)的state變量來實(shí)現(xiàn)鎖的操作。

ThreadLocal

Java中的四種引用類型分別是強(qiáng)軟弱虛。
User user = new User();
在 Java 中最常見的就是強(qiáng)引用,把一個(gè)對(duì)象賦給一個(gè)引用變量,這個(gè)引用變量就是一個(gè)強(qiáng)引用。當(dāng)一個(gè)對(duì)象被強(qiáng)引用變量引用時(shí),它始終處于可達(dá)狀態(tài),它是不可能被垃圾回收機(jī)制回收的,即使該對(duì)象以后永遠(yuǎn)都不會(huì)被用到JVM也不會(huì)回收。因此強(qiáng)引用是造成 Java 內(nèi)存泄漏的主要原因之一。
SoftReference
其次是軟引用,對(duì)于只有軟引用的對(duì)象來說,當(dāng)系統(tǒng)內(nèi)存足夠時(shí)它不會(huì)被回收,當(dāng)系統(tǒng)內(nèi)存空間不足時(shí)它會(huì)被回收。軟引用通常用在對(duì)內(nèi)存敏感的程序中,作為緩存使用。
然后是弱引用,它比軟引用的生存期更短,對(duì)于只有弱引用的對(duì)象來說,只要垃圾回收機(jī)制一運(yùn)行,不管 JVM 的內(nèi)存空間是否足夠,總會(huì)回收該對(duì)象占用的內(nèi)存??梢越鉀Q內(nèi)存泄漏問題,ThreadLocal就是基于弱引用解決內(nèi)存泄漏的問題。
最后是虛引用,它不能單獨(dú)使用,必須和引用隊(duì)列聯(lián)合使用。虛引用的主要作用是跟蹤對(duì)象被垃圾回收的狀態(tài)。不過在開發(fā)中,我們用的更多的還是強(qiáng)引用。

ThreadLocal保證原子性的方式,是不讓多線程去操作臨界資源,讓每個(gè)線程去操作屬于自己的數(shù)據(jù)。

    static ThreadLocal tl1 = new ThreadLocal();static ThreadLocal tl2 = new ThreadLocal();public static void main(String[] args) {tl1.set("123");tl2.set("456");Thread t1 = new Thread(() -> {System.out.println("t1:" + tl1.get());System.out.println("t1:" + tl2.get());});t1.start();System.out.println("main:" + tl1.get());System.out.println("main:" + tl2.get());}

ThreadLocal實(shí)現(xiàn)原理:

  • 每個(gè)Thread中都存儲(chǔ)著一個(gè)成員變量,ThreadLocalMap。
  • ThreadLocal本身不存儲(chǔ)數(shù)據(jù),像是一個(gè)工具類,基于ThreadLocal去操作ThreadLocalMap。
  • ThreadLocalMap本身就是基于Entry[]實(shí)現(xiàn)的,因?yàn)橐粋€(gè)線程可以綁定多個(gè)ThreadLocal,這樣一來,可能需要存儲(chǔ)多個(gè)數(shù)據(jù),所以采用Entry[]的形式實(shí)現(xiàn)。
  • 每一個(gè)現(xiàn)有都自己獨(dú)立的ThreadLocalMap,再基于ThreadLocal對(duì)象本身作為key,對(duì)value進(jìn)行存取ThreadLocalMap的key是一個(gè)弱引用,弱引用的特點(diǎn)是,即便有弱引用,在GC時(shí),也必須被回收。這里是為了在ThreadLocal對(duì)象失去引用后,如果key的引用是強(qiáng)引用,會(huì)導(dǎo)致 ThreadLocal對(duì)象無法被回收。

ThreadLocal內(nèi)存泄漏問題:

  • 如果ThreadLocal引用丟失,key因?yàn)槿跻脮?huì)被GC回收掉,如果同時(shí)線程還沒有被回收,就會(huì)導(dǎo)致內(nèi)存泄漏,內(nèi)* * 存中的value無法被回收,同時(shí)也無法被獲取到。
    只需要在使用完畢ThreadLocal對(duì)象之后,及時(shí)的調(diào)用remove方法,移除Entry即可。
    ThreadLocal

2、可見性

什么是可見性?

可見性問題是基于CPU位置出現(xiàn)的,CPU處理速度非???#xff0c;相對(duì)CPU來說,去主內(nèi)存獲取數(shù)據(jù)這個(gè) 事情太慢了,CPU就提供了L1,L2,L3的三級(jí)緩存,每次去主內(nèi)存拿完數(shù)據(jù)后,就會(huì)存儲(chǔ)到CPU的 三級(jí)緩存,每次去三級(jí)緩存拿數(shù)據(jù),效率肯定會(huì)提升。

這就帶來了問題,現(xiàn)在CPU都是多核,每個(gè)線程的工作內(nèi)存(CPU三級(jí)緩存)都是獨(dú)立的,會(huì)告知每個(gè)線程中做修改時(shí),只改自己的工作內(nèi)存,沒有及時(shí)的同步到主內(nèi)存,導(dǎo)致數(shù)據(jù)不一致問題。
MESI
可見性問題的代碼邏輯:

    private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1線程結(jié)束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主線程將flag改為false");}

解決可見性的方式

volatile

volatile是一個(gè)關(guān)鍵字,用來修飾成員變量。

如果屬性被volatile修飾,相當(dāng)于會(huì)告訴CPU,對(duì)當(dāng)前屬性的操作,不允許使用CPU的緩存,必須去 和主內(nèi)存操作。

volatile的內(nèi)存語(yǔ)義:

  • volatile屬性被寫:當(dāng)寫一個(gè)volatile變量,JMM會(huì)將當(dāng)前線程對(duì)應(yīng)的CPU緩存及時(shí)的刷新到主內(nèi)存中。
  • volatile屬性被讀:當(dāng)讀一個(gè)volatile變量,JMM會(huì)將對(duì)應(yīng)的CPU緩存中的內(nèi)存設(shè)置為無效,必須去主內(nèi)存中重新讀取共享變量。

其實(shí)加了volatile就是告知CPU,對(duì)當(dāng)前屬性的讀寫操作,不允許使用CPU緩存,加了volatile修飾的 屬性,會(huì)在轉(zhuǎn)為匯編之后,追加一個(gè)lock的前綴,CPU執(zhí)行這個(gè)指令時(shí),如果帶有l(wèi)ock前綴會(huì)做兩個(gè)事情:

  • 將當(dāng)前處理器緩存行的數(shù)據(jù)寫回到主內(nèi)存。
  • 這個(gè)寫回的數(shù)據(jù),在其他的CPU內(nèi)核的緩存中,直接無效。
    總結(jié):volatile就是讓CPU每次操作這個(gè)數(shù)據(jù)時(shí),必須立即同步到主內(nèi)存,以及從主內(nèi)存讀取數(shù)據(jù)。
    private volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// ....}System.out.println("t1線程結(jié)束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主線程將flag改為false");}

synchronized

synchronized也是可以解決可見性問題的,synchronized的內(nèi)存語(yǔ)義。
如果涉及到了synchronized的同步代碼塊或者是同步方法,獲取鎖資源之后,將內(nèi)部涉及到的變量從CPU緩存中移除,必須去主內(nèi)存中重新拿數(shù)據(jù),而且在釋放鎖之后,會(huì)立即將CPU緩存中的數(shù)據(jù)同步到主內(nèi)存。

    private static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {synchronized (MiTest.class) {//...}System.out.println(111);}System.out.println("t1線程結(jié)束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主線程將flag改為false");}

Lock

Lock鎖保證可見性的方式和synchronized完全不同,synchronized基于他的內(nèi)存語(yǔ)義,在獲取鎖和釋放鎖時(shí),對(duì)CPU緩存做一個(gè)同步到主內(nèi)存的操作。
Lock鎖是基于volatile實(shí)現(xiàn)的。Lock鎖內(nèi)部再進(jìn)行加鎖和釋放鎖時(shí),會(huì)對(duì)一個(gè)由volatile修飾的state屬性進(jìn)行加減操作。
如果對(duì)volatile修飾的屬性進(jìn)行寫操作,CPU會(huì)執(zhí)行帶有l(wèi)ock前綴的指令,CPU會(huì)將修改的數(shù)據(jù),從 CPU緩存立即同步到主內(nèi)存,同時(shí)也會(huì)將其他的屬性也立即同步到主內(nèi)存中。還會(huì)將其他CPU緩存 行中的這個(gè)數(shù)據(jù)設(shè)置為無效,必須重新從主內(nèi)存中拉取。

    private static boolean flag = true;private static Lock lock = new ReentrantLock();public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {lock.lock();try {//...} finally {lock.unlock();}}System.out.println("t1線程結(jié)束");});t1.start();Thread.sleep(10);flag = false;System.out.println("主線程將flag改為false");}

final

final修飾的屬性,在運(yùn)行期間是不允許修改的,這樣一來,就間接的保證了可見性,所有多線程讀 取final屬性,值肯定是一樣。
final并不是說每次取數(shù)據(jù)從主內(nèi)存讀取,他沒有這個(gè)必要,而且final和volatile是不允許同時(shí)修飾一個(gè)屬性的。
final修飾的內(nèi)容已經(jīng)不允許再次被寫了,而volatile是保證每次讀寫數(shù)據(jù)去主內(nèi)存讀取,并且volatile 會(huì)影響一定的性能,就不需要同時(shí)修飾。
在這里插入圖片描述

3、有序性

什么是有序性?

在Java中,.java文件中的內(nèi)容會(huì)被編譯,在執(zhí)行前需要再次轉(zhuǎn)為CPU可以識(shí)別的指令,CPU在執(zhí)行 這些指令時(shí),為了提升執(zhí)行效率,在不影響最終結(jié)果的前提下(滿足一些要求),會(huì)對(duì)指令進(jìn)行重排。
指令亂序執(zhí)行的原因,是為了盡可能的發(fā)揮CPU的性能。
Java中的程序是亂序執(zhí)行的。

    static int a, b, x, y;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < Integer.MAX_VALUE; i++) {a = 0;b = 0;x = 0;y = 0;Thread t1 = new Thread(() -> {a = 1;x = b;});Thread t2 = new Thread(() -> {b = 1;y = a;});t1.start();t2.start();t1.join();t2.join();if (x == 0 && y == 0) {System.out.println("第" + i + "次,x = " + x + ",y = " + y);}}}

單例模式由于指令重排序可能會(huì)出現(xiàn)問題:
線程可能會(huì)拿到?jīng)]有初始化的對(duì)象,導(dǎo)致在使用時(shí),可能由于內(nèi)部屬性為默認(rèn)值,導(dǎo)致出現(xiàn)一些不必
要的問題。

    private static volatile MiTest test;private MiTest() {}public static MiTest getInstance() { // Bif (test == null) {synchronized (MiTest.class) {if (test == null) {// A , 開辟空間,test指向地址,初始化test = new MiTest();}}}return test;}

as-if-serial

as-if-serial語(yǔ)義: 不論指定如何重排序,需要保證單線程的程序執(zhí)行結(jié)果是不變的。 而且如果存在依賴的關(guān)系,那么也不可以做指令重排。

// 這種情況肯定不能做指令重排序 int i = 0;
i++;
// 這種情況肯定不能做指令重排序 int j = 200;
j * 100;
j + 100;
// 這里即便出現(xiàn)了指令重排,也不可以影響最終的結(jié)果,20100

happens-before

具體規(guī)則:

  1. 單線程happen-before原則:在同一個(gè)線程中,書寫在前面的操作happen-before后面的操作。
  2. 鎖的happen-before原則:同一個(gè)鎖的unlock操作happen-before此鎖的lock操作。
  3. volatile的happen-before原則:對(duì)一個(gè)volatile變量的寫操作happen-before對(duì)此變量的任意操作。
  4. happen-before的傳遞性原則:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
  5. 線程啟動(dòng)的happen-before原則:同一個(gè)線程的start方法happen-before此線程的其它方法。
  6. 線程中斷的happen-before原則:對(duì)線程interrupt方法的調(diào)用happen-before被中斷線程的檢測(cè)到中斷發(fā)送的代碼。
  7. 線程終結(jié)的happen-before原則:線程中的所有操作都happen-before線程的終止檢測(cè)。
  8. 對(duì)象創(chuàng)建的happen-before原則:一個(gè)對(duì)象的初始化完成先于他的finalize方法調(diào)用。

JMM只有在不出現(xiàn)上述8中情況時(shí),才不會(huì)觸發(fā)指令重排效果。不需要過分的關(guān)注happens-before原則,只需要可以寫出線程安全的代碼就可以了。

volatile

如果需要讓程序?qū)δ骋粋€(gè)屬性的操作不出現(xiàn)指令重排,除了滿足happens-before原則之外,還可以基于volatile修飾屬性,從而對(duì)這個(gè)屬性的操作,就不會(huì)出現(xiàn)指令重排的問題了。

volatile如何實(shí)現(xiàn)的禁止指令重排?
內(nèi)存屏障概念。將內(nèi)存屏障看成一條指令。 會(huì)在兩個(gè)操作之間,添加上一道指令,這個(gè)指令就可以避免上下執(zhí)行的其他指令進(jìn)行重排序。

http://www.risenshineclean.com/news/115.html

相關(guān)文章:

  • 石家莊網(wǎng)絡(luò)公司排名/南昌seo公司
  • 免費(fèi)的行情軟件網(wǎng)站在線使用/企業(yè)網(wǎng)站建設(shè)多少錢
  • 山東省建設(shè)工程注冊(cè)中心網(wǎng)站/114黃頁(yè)
  • 手機(jī)網(wǎng)站后臺(tái)/seo是什么?
  • 做黨和人民滿意的好教師PPT網(wǎng)站/百度瀏覽器廣告怎么投放
  • 中國(guó)百?gòu)?qiáng)城市榜單排名/seo sem推廣
  • 南寧機(jī)關(guān)兩學(xué)一做網(wǎng)站/網(wǎng)絡(luò)營(yíng)銷八大工具
  • 天津市建設(shè)工程信息交易網(wǎng)/seo免費(fèi)培訓(xùn)教程
  • 做餐飲要看的網(wǎng)站/seo批量建站
  • 北京做網(wǎng)站浩森宇特/品牌推廣軟文200字
  • 靖江網(wǎng)站建設(shè)/百度小說風(fēng)云榜今天
  • 漳州專業(yè)網(wǎng)站建設(shè)費(fèi)用/青島seo用戶體驗(yàn)
  • 天津做網(wǎng)站要多少錢/百度seo流量
  • 專業(yè)建設(shè)網(wǎng)站公司哪家好/優(yōu)化深圳seo
  • 企業(yè)網(wǎng)站服務(wù)器選擇/torrentkitty磁力官網(wǎng)
  • 石獅市網(wǎng)站建設(shè)/seo 頁(yè)面鏈接優(yōu)化
  • 上海網(wǎng)站排名團(tuán)隊(duì)/百度搜索引擎的網(wǎng)址是
  • ds216j做網(wǎng)站/跨境電商培訓(xùn)
  • 微網(wǎng)站 pc網(wǎng)站同步/長(zhǎng)沙靠譜關(guān)鍵詞優(yōu)化公司電話
  • 人設(shè)生成器網(wǎng)站/怎么把平臺(tái)推廣出去
  • 溧陽(yáng)手機(jī)網(wǎng)站哪里做/學(xué)it什么培訓(xùn)機(jī)構(gòu)好
  • 找人網(wǎng)站/網(wǎng)頁(yè)模板怎么用
  • 揭陽(yáng)網(wǎng)站制作案例/如何在各種網(wǎng)站投放廣告
  • 網(wǎng)站免費(fèi)正能量入口/百度首頁(yè)推薦關(guān)不掉嗎
  • 網(wǎng)站建設(shè)步驟及分工/優(yōu)秀軟文范例100字
  • 對(duì)網(wǎng)站進(jìn)行優(yōu)化/網(wǎng)站排名快速提升
  • 武城網(wǎng)站建設(shè)費(fèi)用/品牌營(yíng)銷公司
  • 佛山市企業(yè)網(wǎng)站建設(shè)平臺(tái)/太原關(guān)鍵詞優(yōu)化軟件
  • 做網(wǎng)站大概價(jià)格/南昌seo排名優(yōu)化
  • 做網(wǎng)站的公司有前途嗎/如何制作網(wǎng)站和網(wǎng)頁(yè)