真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

我看誰(shuí)還不懂多線程之間的通信+基礎(chǔ)入門+實(shí)戰(zhàn)教程+詳細(xì)介紹+附源碼

一、多線程之間的通信(Java版本)

1、多線程概念介紹

多線程概念

創(chuàng)新互聯(lián)公司是一家專業(yè)提供南關(guān)企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)、成都網(wǎng)站設(shè)計(jì)、H5場(chǎng)景定制、小程序制作等業(yè)務(wù)。10年已為南關(guān)眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。

  • 在我們的程序?qū)用鎭碚f,多線程通常是在每個(gè)進(jìn)程中執(zhí)行的,相應(yīng)的附和我們常說的線程與進(jìn)程之間的關(guān)系。線程與進(jìn)程的關(guān)系:線程可以說是進(jìn)程的兒子,一個(gè)進(jìn)程可以有多個(gè)線程。但是對(duì)于線程來說,只屬于一個(gè)進(jìn)程。再說說進(jìn)程,每個(gè)進(jìn)程的有一個(gè)主線程作為入口,也有自己的唯一標(biāo)識(shí)PID,它的PID也就是這個(gè)主線程的線程ID。

  • 對(duì)于我們的計(jì)算機(jī)硬件來說,線程是進(jìn)程中的一部分,也是進(jìn)程的的實(shí)際運(yùn)作單位,它也是操作系統(tǒng)中的最小運(yùn)算調(diào)度單位。多線程可以提高CPU的處理速度。當(dāng)然除了單核CPU,因?yàn)閱魏诵腃PU同一時(shí)間只能處理一個(gè)線程。在多線程環(huán)境下,對(duì)于單核CP來說,并不能提高響應(yīng)速度,而且還會(huì)因?yàn)轭l繁切換線程上下文導(dǎo)致性能降低。多核心CPU具有同時(shí)并行執(zhí)行線程的能力,因此我們需要注意使用環(huán)境。線程數(shù)超出核心數(shù)時(shí)也會(huì)引起線程切換,并且操作系統(tǒng)對(duì)我們線程切換是隨機(jī)的。

2、線程之間如何通信

引入

  • 對(duì)于我們Java語(yǔ)言來說,多線程編程也是它的特性之一。我們需要利用多線程操作同一共享資源,從而實(shí)現(xiàn)一些特殊任務(wù)。上面說了,多線程在進(jìn)行切換時(shí)CPU隨機(jī)調(diào)度的,假如我們直接運(yùn)行多個(gè)線程操作共享資源的話,勢(shì)必會(huì)引起一些不可控錯(cuò)誤因素。
  • 接下來,我們就需要讓這些不可控變?yōu)榭煽?!這個(gè)時(shí)候就引出了本文的重點(diǎn)線程通信。線程通信就是為了解決多線程對(duì)同一共享變量的爭(zhēng)奪。

Java 線程通信的方式

  • 共享內(nèi)存機(jī)制
    • 比如說Java的volatile關(guān)鍵字就是基于內(nèi)存屏障解決變量的可見性,從而實(shí)現(xiàn)其他線程訪問共享變量都是必須從主存中獲?。▽?duì)應(yīng)其他線程對(duì)變量的更新也得及時(shí)的刷新到主存)。
    • synchronized 關(guān)鍵字基于對(duì)象鎖這種方式實(shí)現(xiàn)線程互斥,可以通知對(duì)方有其他的線程正在執(zhí)行這部分代碼。
  • 消息傳遞模式
    • wait() 和 notify()/notifyAll() 等待通知方式實(shí)現(xiàn)線程的阻塞就緒狀態(tài)之間的轉(zhuǎn)換。
    • park、unpark
    • join() 阻塞【底層也是依賴wait實(shí)現(xiàn)】。
    • interrupt()打斷阻塞狀態(tài)。
    • 管道輸入/輸出。

3、線程通信方法詳細(xì)介紹

主要介紹wait/notify,也有ReentrantLock的Condition條件變量的await/signal,LockSupport的park/unpark方法,也能實(shí)現(xiàn)線程之間的通信。主要是阻塞/喚醒通信模式。

首先說明這種方法一般都是作用于調(diào)用方法的所在線程。比如在主線程執(zhí)行wait方法,就是將主線程阻塞了。

wait/notify機(jī)制

  • wait()、notify方法在Java中是Object提供給我們的。又因?yàn)樗械念惗寄J(rèn)隱式繼承了Object類,進(jìn)而我們的每一個(gè)對(duì)象都具有wait和notify。
    • wait方法含義:一個(gè)線程一旦調(diào)用了任意對(duì)象obj.wait()方法,它就釋放了所持有的監(jiān)視器對(duì)象(obj)上的鎖,并轉(zhuǎn)為非運(yùn)行狀態(tài)(阻塞)。
    • notify方法含義:一個(gè)線程若執(zhí)行obj.notify方法,則隨機(jī)喚醒obj對(duì)象上監(jiān)視器(操作系統(tǒng)也稱為管程)monitor的阻塞隊(duì)列waitset中一個(gè)線程。
    • wait和notify方法的使用同時(shí)必須配合synchronized關(guān)鍵字使用。同時(shí)也需要成對(duì)出現(xiàn)。就是說wait和notify必須得在同步代碼塊內(nèi)部使用,大致原因就是需要保證同時(shí)只有一個(gè)線程可以去執(zhí)行wait,使該線程阻塞。

await/signal

  • 要想使用await/signal首先是需要借用Condition條件變量,要想獲取Condition條件變量,就必須通過ReentrantLock鎖獲取。
  • ReentrantLock和Synchronized類似,都是可重入鎖,并且大多都是當(dāng)做重量級(jí)鎖使用。
    • 區(qū)別:ReentrantLock是API層面實(shí)現(xiàn)的,我們可以根據(jù)自己隨意調(diào)用定制,但是Synchronized是JVM底層實(shí)現(xiàn),我們無需關(guān)心他上鎖解鎖的流程。
  • await/signal使用時(shí)需要配合ReentrantLock鎖對(duì)象的lock和unlock方法加鎖解鎖。就像wait/notify在synchronized在同步代碼塊中使用一樣。他們都需要保證當(dāng)前線程是唯一執(zhí)行這段邏輯的線程。防止出現(xiàn)多線程造成的線程安全問題。

park/unpark

二、線程通信過程中需要注意的問題

1、喚醒丟失

如果一個(gè)線程先于被通知線程調(diào)用wait()前調(diào)用了notify(),等待的線程將錯(cuò)過這個(gè)信號(hào)。

  • 喚醒丟失主要是在我們使用wait 和 notify的過程中的時(shí)序問題。比如說我們線程二在執(zhí)行某個(gè)對(duì)象notify的時(shí)候,線程一還沒有執(zhí)行該對(duì)象的wait方法。那么這次的喚醒就會(huì)丟失,我們就不能讓線程二得notify方法起作用,自然而然線程一就不會(huì)被喚醒。
  • 舉個(gè)例子吧,這就好比我們平常在宿舍每天都會(huì)有叫醒服務(wù),但是這次 因?yàn)橐恍┰颍ㄍㄏぁぁぃ┪乙徽矶紱]有睡覺,而且當(dāng)?shù)诙煸缟系慕行逊?wù)來的 時(shí)候也是醒著的。那么叫醒服務(wù)就會(huì)以為你已經(jīng)醒來了,就會(huì)視而不見。沒想到吧,叫醒服務(wù)剛走我就躺下來睡著了,所以我錯(cuò)過了這次叫醒服務(wù)。就能好好的睡覺了。這看起來沒有什么大問題,但是你仔細(xì)想想若是每個(gè)睡著的人都需要被叫醒服務(wù)才能醒過來,外加上只有一次叫醒服務(wù)的機(jī)會(huì)。那么你就可以沉睡萬年了,開心不。
  • 哈哈哈···
  • 這在程序中也是一樣 的,如果錯(cuò)過notify那么就會(huì)一直wait。
    • 所以我們必須預(yù)防這種問題,比如說每隔一段時(shí)間去喚醒,也就是隔兩分鐘就去叫醒睡著的人。但是這種缺點(diǎn)就是太累了,對(duì)于程序來說是消耗性能和內(nèi)存。實(shí)現(xiàn)也簡(jiǎn)單就是寫入while循環(huán)體中,不停地嘗試即可。
    • 我們也可以使用一個(gè)標(biāo)志位完美的實(shí)現(xiàn)。初始化設(shè)置flag=FALSE表示還沒wait,在wait之前將設(shè)置flag=TRUE,在notify之后設(shè)置flag=FALSE。每次notify喚醒之前都判斷flag=true是否已經(jīng)wait,在wait中判斷flag=false是否已經(jīng)notify。

核心代碼演示

  • 首先使用線程池創(chuàng)建線程一使自己進(jìn)入阻塞態(tài),然后再調(diào)用LOCK1的notify方法喚醒線程一
	    // 線程一使用LOCK1對(duì)象調(diào)用wait方法阻塞自己
        executor.execute(new ThreadTest("線程一",LOCK1,LOCK2));

        synchronized (LOCK1) {
            System.out.println("main執(zhí)行notify方法讓線程一醒過來");
            LOCK1.notify();
        }
  • 但是他很有可能醒不來,因?yàn)橹骶€程調(diào)用LOCK1對(duì)象的notify方法,可能主線程已經(jīng)執(zhí)行完了,上面線程還沒創(chuàng)建完成,也就是沒有進(jìn)入wait狀態(tài)。就醒不來了。

  • 解決方式:使用信號(hào)量標(biāo)志進(jìn)行判斷是否已經(jīng)進(jìn)入wait

            synchronized (LOCK1) {
                while (true) {
                    if (FLAG.getFlag()) {
                        System.out.println("main馬上執(zhí)行notify方法讓線程一醒過來" + "flag = " + FLAG.getFlag());
                        LOCK1.notify();
                        // 將標(biāo)志位變?yōu)镕ALSE
                        FLAG.setFlag(Constants.WaitOrNoWait.NO_WAIT.getFlag());
                        System.out.println("main執(zhí)行notify方法完畢" + "flag = " + FLAG.getFlag());
                        break;
                    }
                }
            }
    

2、假喚醒

由于莫名其妙的原因,線程有可能在沒有調(diào)用過notify()和notifyAll()的情況下醒來。

  • 其實(shí)在上面的代碼中已經(jīng)解決了假喚醒的問題,因?yàn)槲覀冎恍枰粩嗳L試獲取標(biāo)志位信息即可。

3、多線程喚醒

  • 多個(gè)線程執(zhí)行時(shí),防止notifyAll全部喚醒之后就結(jié)束運(yùn)行,我們的需求是只能喚醒一個(gè)線程,當(dāng)其他線程被喚醒之后需要重新判斷標(biāo)志位是否為FALSE,也就是需要判斷是否有其他線程執(zhí)行了喚醒操作,因?yàn)橐淮沃荒芙行岩粋€(gè)人,需要排隊(duì),他們就可以繼續(xù)自旋判斷。
		synchronized (waitName) {
            while (!flag.getFlag()) {
                try {
                    // 將標(biāo)志位設(shè)置為TRUE
                    flag.setFlag(Constants.WaitOrNoWait.WAIT.getFlag());
                    System.out.println("name;"+name+" 我睡著了進(jìn)入阻塞狀態(tài)" + "flag = " + flag.getFlag());
                    waitName.wait();
                    System.out.println("name;"+name+" 我醒來了" + "flag = " + flag.getFlag());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
  • 大家如果使用的是new Thread()方式創(chuàng)建線程的話,要想保證安全的話還可以給該標(biāo)志位加上volatile關(guān)鍵字,可以時(shí)刻保證該標(biāo)志位的可見性。
  • 我這里使用的標(biāo)志位是使用傳遞引用的方式,使用同一個(gè)對(duì)象,將標(biāo)志位定義為該對(duì)象中的屬性,然后再結(jié)合枚舉類進(jìn)行設(shè)置標(biāo)志位的值。因?yàn)槲沂褂镁€程池創(chuàng)建對(duì)象,并且自定義線程類,這里是無法設(shè)置全局變量,傳遞給線程類。包裝類也不行哦。(感興趣可以親自試一下)
  • 大體代碼結(jié)構(gòu)如下所示:
	private final static Object LOCK1 = new Object();
    private final static Object LOCK2 = new Object();
    private final  static Constants.WaitStatus FLAG = new Constants.WaitStatus(false);
    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 5, 1, TimeUnit.DAYS, new ArrayBlockingQueue<>(4), new ThreadPoolExecutor.AbortPolicy());
        executor.execute(new ThreadTest("線程一",LOCK1,LOCK2, FLAG));
        // ···喚醒
    }

class ThreadTest implements Runnable { //阻塞··· }

完整代碼可以看這[Gitee倉(cāng)庫(kù)完整代碼][https://gitee.com/malongfeistudy/javabase/tree/master/Java多線程_Study/src/main/java/com/mlf/thread/demo_wait_notify]

三、線程通信實(shí)戰(zhàn)

前置知識(shí):線程池的使用方法

  • 首先復(fù)習(xí)一下創(chuàng)建線程的幾種方式和其的優(yōu)缺點(diǎn):

    • 通過new Thread()
    • 繼承Thread():和new Thread沒啥區(qū)別,就是耦合度低了
      • 定義線程類繼承Thread類并且重寫run方法即可。
      • 優(yōu)點(diǎn)是簡(jiǎn)潔方便
      • 缺點(diǎn)是占用了該類的單繼承位置,無法繼承其他父類
    • 實(shí)現(xiàn)Runnable接口
    • 實(shí)現(xiàn)Callable接口
      • 和實(shí)現(xiàn)Runnable接口類似
      • 優(yōu)點(diǎn):
        • 實(shí)現(xiàn)接口,不占用繼承的位置;
        • 耦合度降低,并且可定化程度提高。各個(gè)模塊之間的調(diào)用關(guān)系更加清晰
      • 缺點(diǎn):
        • 實(shí)現(xiàn)起來稍微麻煩
  • 使用線程池的步驟

    • 線程池初始化方式:
      • 使用Executor初始化線程池
        • 優(yōu)點(diǎn):方便快捷,適用于自己測(cè)試時(shí)使用
        • 缺點(diǎn):在實(shí)際開發(fā)中無法判斷細(xì)節(jié)
      • new ThreadPoolExecutor()構(gòu)造器創(chuàng)建(本文使用方式)
        • 優(yōu)點(diǎn):可以清晰地定制出適合自己的線程池,不會(huì)造成資源浪費(fèi)
        • 缺點(diǎn):麻煩
  • 在主線程自定義線程池使用實(shí)例,這里需要根據(jù)實(shí)際情況定義鎖對(duì)象,因?yàn)槲覀冃枰褂眠@些鎖對(duì)象控制多線程之間的運(yùn)行順序以及線程之間的通信。在Java中每個(gè)對(duì)象都會(huì)在初始化的時(shí)候擁有一個(gè)監(jiān)視器,我們需要利用好他進(jìn)行并發(fā)編程。這種創(chuàng)建線程池的方法也是阿里巴巴推薦的方式,想想以阿里的體量多年總結(jié)出來的總沒有錯(cuò),大家還是提前約束自己的編碼習(xí)慣等。安裝一個(gè)阿里代碼規(guī)范的插件對(duì)自己的程序員道路是比較nice的。

    /**
     * 每個(gè)使用對(duì)應(yīng)唯一的對(duì)象作為監(jiān)視器對(duì)象鎖。
     */
    public static final Object A_O = new Object();
    public static final Object B_O = new Object(); 
        /** 參數(shù):
         * int corePoolSize,                     核心線程數(shù)
         * int maximumPoolSize,                  最大線程數(shù)
         * long keepAliveTime,                   救急存活時(shí)間
         * TimeUnit unit,                        單時(shí)間位
         * BlockingQueue workQueue,    阻塞隊(duì)列
         * RejectedExecutionHandler handler      拒絕策略
         **/
        // 使用阿里巴巴推薦的創(chuàng)建線程池的方式
        // 通過ThreadPoolExecutor構(gòu)造函數(shù)自定義參數(shù)創(chuàng)建
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                3,
                5,
                1,
                TimeUnit.DAYS,
                new ArrayBlockingQueue<>(2),
                new ThreadPoolExecutor.AbortPolicy());
  • 接下來需要自定義線程類,我們可以自定義線程類,并且在該線程類中定義自己需要的共享資源(鎖對(duì)象,屬性等),在run方法中寫盡自己的線程運(yùn)行邏輯即可。
class ThreadDiy implements Runnable {

    private final String name;

    /**
     * 阻塞鎖對(duì)象  等待標(biāo)記
     **/
    private final Object waitFor;

    /**
     * 執(zhí)行鎖對(duì)象  下一個(gè)標(biāo)記
     **/
    private final Object next;

    public AlternateThread(String name, Object waitFor, Object next) {
    }

    @Override
    public void run() {
        // 線程的代碼邏輯···
    }

}

1、控制兩個(gè)線程之間的執(zhí)行順序

題目:現(xiàn)在有兩個(gè)線程,不論線程的啟動(dòng)順序,我需要指定線程一先執(zhí)行,然后線程二再執(zhí)行。

  • 初始化兩個(gè)對(duì)象鎖作為線程監(jiān)視器。

        private final static Object ONE_LOCK = new Object();
        private final static Object TWO_LOCK = new Object();
    
  • 接下來初始化線程池,上面有具體的介紹,在這就不多說了

  • 使用線程池去執(zhí)行我們的兩個(gè)線程,在這里我們需要分析的是

        // 使用線程池創(chuàng)建線程
        executor.execute(new DiyThread(1, ONE_LOCK, TWO_LOCK));
        executor.execute(new DiyThread(2, TWO_LOCK, ONE_LOCK));

        synchronized (ONE_LOCK) {
            ONE_LOCK.notify();
        }

創(chuàng)建線程類

  • 我們使用繼承Runnable的方式去創(chuàng)建線程對(duì)象,需要在這個(gè)類中實(shí)現(xiàn)每個(gè)線程執(zhí)行的邏輯,我們根據(jù)題目可以得出,我們要控制每個(gè)線程的執(zhí)行順序,怎么辦?那么就要實(shí)現(xiàn)所有線程之間的通信,通信方式采用wait-notify的方式即可。我們使用wait-notify的時(shí)候必須結(jié)合synchronized,那么就需要控制兩個(gè)對(duì)象鎖。因?yàn)槲覀儾还馐强刂谱约?,還有另一個(gè)線程。

  • 我們?cè)俜治鲆幌骂}意,首先需要指定先后執(zhí)行的順序,那么就需要實(shí)現(xiàn)兩個(gè)線程之間的通信。其次呢,我們得控制兩個(gè)線程,那么就需要兩個(gè)監(jiān)視器去監(jiān)視這兩個(gè)線程。

  • 我們定義這兩個(gè)監(jiān)視器對(duì)象為own和other。然后再新增一個(gè)屬性threadId來標(biāo)識(shí)自己。

        private final int threadId;
        private final Object own;
        private final Object other;
    
  • 接下來就是編寫Run方法了

  • 每個(gè)線程首先需要阻塞自己,等待喚醒。然后喚醒之后,再去喚醒另外一個(gè)線程。這樣就實(shí)現(xiàn)了自定義順序。至于先喚醒哪個(gè)線程,交給我們的主線程去完成。

  • 這里需要注意的是,如果我們只是單純地執(zhí)行了多個(gè)線程對(duì)象,但是主線程沒有主動(dòng)去喚醒其中一個(gè),這樣就會(huì)形成類似于死鎖的循環(huán)等待。你需要我喚醒,我需要你喚醒。這個(gè)時(shí)候需要主線程去插手喚醒其中的任意一個(gè)線程。

    • 第一步阻塞自己own

              synchronized (own) {
                  try {
                      own.wait();
                      System.out.println(num);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
      
    • 第二步喚醒other

              synchronized (other) {
                  other.notify();
              }
      

2、多線程交替打印輸出

題目需求:現(xiàn)在需要使用三個(gè)線程輪流打印輸出。說白了也就是多線程輪流執(zhí)行罷了,和問題一控制兩個(gè)線程打印順序沒什么區(qū)別

  • 還是老步驟,首先需要定義線程類,我們需要控制當(dāng)前線程和下一個(gè)線程即可。我們這里需要兩個(gè)對(duì)象,一個(gè)是阻塞鎖對(duì)象用來阻塞當(dāng)前線程。另一個(gè)是喚醒鎖對(duì)象,用來喚醒下一個(gè)對(duì)象。
    /**
     * 阻塞鎖對(duì)象  等待標(biāo)記
     **/
    private final Object waitFor;
    /**
     * 喚醒鎖對(duì)象  下一個(gè)標(biāo)記
     **/
    private final Object next;
  • run方法的邏輯和上面的基本一樣。 一個(gè)線程一旦調(diào)用了任意對(duì)象的wait()方法,它就釋放了所持有的監(jiān)視器對(duì)象上的鎖,并轉(zhuǎn)為非運(yùn)行狀態(tài)。

  • 每個(gè)線程首先會(huì)調(diào)用 waitFor對(duì)象的 wait()方法,隨后該線程進(jìn)入阻塞狀態(tài),等待其他線程執(zhí)行自己引用的該 waitFor對(duì)象的 notify()方法即可。

    		while (true) {
                synchronized (waitFor) {
                    try {
                        waitFor.wait();
                        System.out.println(name + " 開始執(zhí)行");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                synchronized (next) {
                    next.notify();
                }
            }
    
  • 主線程需要初始化線程池、執(zhí)行三個(gè)線程,并且最后需要打破僵局,因?yàn)榇藭r(shí)每個(gè)線程都是阻塞狀態(tài),他們沒法阻塞/喚醒循環(huán)下去。

            synchronized (A_O) {
                A_O.notify();
            }
    
  • 模擬執(zhí)行流程

/**
 * 模擬執(zhí)行流程
 * 打印名(name)    等待標(biāo)記(waitFor)   下一個(gè)標(biāo)記(next)
 *      1                 A                  B
 *      2                 B                  C
 *      3                 C                  A
 * 
 * 像不像Spring的循環(huán)依賴:確實(shí)很像,Spring中的循環(huán)依賴就是 BeanA 依賴 BeanB,BeanB 依賴 BeanA;
 * 他們實(shí)例化過程中都需要先屬性注入對(duì)方的實(shí)例,倘若剛開始的時(shí)候都沒有實(shí)例化,初始化就會(huì)死等。類似于死鎖。
 **/

3、多線程順序打印同一個(gè)自增變量

使用多線程輪流打印 01234····

  • 思路:使用自增原子變量AtomicInteger和多線程配合打印。

具體代碼請(qǐng)移步到Gitee倉(cāng)庫(kù):[順序打印自增變量][https://gitee.com/malongfeistudy/javabase/blob/master/Java多線程_Study/src/main/java/com/mlf/thread/print/AddNumberPrint2.java]

條件變量Condition的使用

  • Condition是一個(gè) LOCK 實(shí)例出來的,他們獲取的都是一個(gè) LOCK 的鎖,而如果要調(diào)用 object的 wait和notify 方法,首先要獲取對(duì)應(yīng)的object的鎖,如果要調(diào)用Condition 的await、signal方法,必須先獲取Lock鎖(Lock.lock)。
  • 多線程的初衷就是操作共享資源,然后我們需要保證共享資源同一時(shí)刻只能被一個(gè)線程所修改。那么就需要一把鎖來控制這些線程之間互斥條件。這里使用一個(gè)ReentrantLock鎖作為我們的Lock對(duì)象。通過同一個(gè) Lock鎖 獲取的每個(gè)Condition 就可以作為每個(gè)線程自己的阻塞條件和喚醒條件。

如有問題,請(qǐng)留言評(píng)論。


名稱欄目:我看誰(shuí)還不懂多線程之間的通信+基礎(chǔ)入門+實(shí)戰(zhàn)教程+詳細(xì)介紹+附源碼
轉(zhuǎn)載源于:http://weahome.cn/article/dsoidso.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部