程序、進程、線程


1、程序(program):是為完成特定任務、用某種語言編寫的一組指令的集合。即指一 段靜態的代碼,靜態對象。

2、進程(process):是程序的一次執行過程,或是正在運行的一個程序。是一個動態的過程:有它自身的產生、存在和消亡的過程。—生命周期

  1)進程作為資源分配的單位,系統在運行時會為每個進程分配不同的內存區域

3、線程(thread),進程可進一步細化為線程,是一個程序內部的一條執行路徑。

  1)若一個進程同一時間並行執行多個線程,就是支持多線程的

  2)線程作為調度和執行的單位,每個線程擁有獨立的運行棧和程序計數器(pc),線程切換的開銷小

  3)一個進程中的多個線程共享相同的內存單元/內存地址空間它們從同一堆中分配對象,可以 訪問相同的變量和對象。這就使得線程間通信更簡便、高效。但多個線程操作共享的系統資源可能就會帶來安全的隱患

 

一個Java應用程序java.exe,其實至少有三個線程:main()主線程,gc() 垃圾回收線程,異常處理線程。當然如果發生異常,會影響主線程。

並行與並發: 

  1) 並行:多個CPU同時執行多個任務。比如:多個人同時做不同的事。

  2) 並發:一個CPU(采用時間片)同時執行多個任務。比如:秒殺、多個人做同一件事。

多線程程序的優點:

  1) 提高應用程序的響應。對圖形化界面更有意義,可增強用戶體驗。

  2) 提高計算機系統CPU的利用率

  3) 改善程序結構。將既長又復雜的進程分為多個線程,獨立運行,利於理解和修改

何時需要多線程

  1)程序需要同時執行兩個或多個任務。

  2)程序需要實現一些需要等待的任務時,如用戶輸入、文件讀寫操作、網絡操作、搜索等。

  3)需要一些后台運行的程序時。

線程的創建和啟動

Java語言的JVM允許程序運行多個線程,它通過java.lang.Thread 類來體現。

  1)每個線程都是通過某個特定Thread對象的run()方法來完成操作的,經常把run()方法的主體稱為線程體

  2)通過該Thread對象的start()方法來啟動這個線程,而非直接調用run()。

Thread類構造器

  1)Thread():創建新的Thread對象;

  2)Thread(String threadname):創建線程並指定線程實例名;

  3)Thread(Runnable target):指定創建線程的目標對象,它實現了Runnable接 口中的run方法;

  4)Thread(Runnable target, String name):創建新的Thread對象。


 

API中創建線程的兩種方式

JDK1.5之前創建新執行線程有兩種方法:

  1)繼承Thread類的方式;

  2)實現Runnable接口的方式;

1):繼承Thread類

1)  定義子類繼承Thread類。 
2)  子類中重寫Thread類中的run方法。
3)  創建Thread子類對象,即創建了線程對象。 
4)  調用線程對象start方法:啟動線程,調用run方法。
package com.thread.test;

public class ThreadTest {
    public static void main(String[] args) {
        //3 創建Thread子類對象,即創建了線程對象。 
        MyThread m=new MyThread();
        //4 調用線程對象start方法:啟動線程,調用run方法。
        m.start();
    }
}
//1 定義子類繼承Thread類。
class  MyThread extends  Thread{
    //2 子類中重寫Thread類中的run方法。
    @Override
    public  void  run(){
        for (int i = 0; i <100 ; i++) {
            System.out.println("子線程"+i);
        }
    }
}

2):實現Runnable接口

1) 定義子類,實現Runnable接口。 
2) 子類中重寫Runnable接口中的run方法。 
3) 通過Thread類含參構造器創建線程對象。 
4) 將Runnable接口的子類對象作為實際參數傳遞給Thread類的構造器中。 
5) 調用Thread類的start方法:開啟線程,調用Runnable子類接口的run方法。
package com.thread.test;

public class ThreadTest {
    public static void main(String[] args) {

//        3) 通過Thread類含參構造器創建線程對象。------------------------------創建實現類的對象
//        4) 將Runnable接口的子類對象作為實際參數傳遞給Thread類的構造器中。-------將此對象作為參數傳遞Thread類構造器中,創建Thread類的對象
//        5) 調用Thread類的start方法:開啟線程,調用Runnable子類接口的run方法。---通過Thread類的對象調用start();
        new Thread(new MyThread2()).start();
    }
}

    // 1) 定義子類,實現Runnable接口。
class  MyThread2 implements  Runnable{
    //2) 子類中重寫Runnable接口中的run方法。
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println("子線程"+i);
        }
    }
}

Thread類的有關方法

void start():  啟動線程,並執行對象的run()方法 
run():  線程在被調度時執行的操作 
String getName():  返回線程的名稱 
void setName(String name):設置該線程名稱 
static Thread currentThread(): 返回當前線程。在Thread子類中就是this,通常用於主線程和Runnable實現類
static  void  yield():線程讓步 
  暫停當前正在執行的線程,把執行機會讓給優先級相同或更高的線程 
  若隊列中沒有同優先級的線程,忽略此方法 
join() :當某個程序執行流中調用其他線程的 join() 方法時,調用線程將 被阻塞,直到 join() 方法加入的 join 線程執行完為止 
  低優先級的線程也可以獲得執行 
static  void  sleep(long millis):(指定時間:毫秒) 
  令當前活動線程在指定時間段內放棄對CPU控制,使其他線程有機會被執行,時間到后 重排隊。 
  拋出InterruptedException異常 
stop(): 強制線程生命期結束,不推薦使用 
boolean isAlive():返回boolean,判斷線程是否還活着

線程的調度

1)調度策略

2)調度方法

  1.同優先級線程組成先進先出隊列(先到先服務),使用時間片策略

  2.對高優先級,使用優先調度的搶占式策略

3)線程的優先級

  1.等級,方法

MAX_PRIORITY:10 
MIN _PRIORITY:1 
NORM_PRIORITY:5 
getPriority() :返回線程優先值 
setPriority(int newPriority) :改變線程的優先級

  線程創建時繼承父線程的優先級 

  低優先級只是獲得調度的概率低,並非一定是在高優先級線程之后才被調用

  Java中的線程分為兩類:一種是守護線程,一種是用戶線程。

  守護線程是用來服務用戶線程的,通過在start()方法前調用 thread.setDaemon(true)可以把一個用戶線程變成一個守護線程。

  Java垃圾回收就是一個典型的守護線程。


線程的生命周期

多線程出現了安全問題

  當多條語句在操作同一個線程共享數據時,一個線程對多條語句只執行了一部分,還沒有 執行完,另一個線程參與進來執行。導致共享數據的錯誤。

  對多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中,其他線程不可以 參與執行。

線程的同步:Synchronized同步機制

同步機制中的鎖:

在《Thinking in Java》中,是這么說的:對於並發工作,你需要某種方式來防 止兩個任務訪問相同的資源(其實就是共享資源競爭)。 防止這種沖突的方法 就是當資源被一個任務使用時,在其上加鎖。第一個訪問某項資源的任務必須 鎖定這項資源,使其他任務在其被解鎖之前,就無法訪問它了,而在其被解鎖 之時,另一個任務就可以鎖定並使用它了。

 synchronized的鎖是什么? 

  任意對象都可以作為同步鎖。

  所有對象都自動含有單一的鎖(監視器)。 

  同步方法的鎖:靜態方法(類名.class)、非靜態方法(this) 

  同步代碼塊:自己指定,很多時候也是指定為this或類名.class

必須確保使用同一個資源的多個線程共用一把鎖,這個非常重要,否則就 無法保證共享資源的安全 

  1)同步方法仍然涉及到同步監視器,只是不需要我們顯式的聲明。一個線程類中的所有靜態方法共用同一把鎖(類名.class),所有非靜態方法共用同一把鎖(this),

  2)同步代碼塊(指定需謹慎)。對於實現 Runnable 接口的方法,由於就一個實現類對象,所以可以用this 當鎖。 對於繼承Thread 類的方法,由於有多個子類的實例,所以需要使用 類名.class 當鎖。

同步的方式,解決了線程的安全問題。---好處

操作同步代碼時,只能有一個線程參與,其他線程等待。相當於是一個單線程的過程,效率低。 ---局限性

線程的通信:

* 涉及到的三個方法:
* wait():一旦執行此方法,當前線程就進入阻塞狀態,並釋放同步監視器。
* notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
* notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。
*
* 說明:
* 1.wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
* 2.wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步監視器。當前線程必須具有對該對象的監控權(加鎖)
* 否則,會出現IllegalMonitorStateException異常
* 3.wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。
*
* 面試題:sleep() 和 wait()的異同?
* 1.相同點:一旦執行方法,都可以使得當前的線程進入阻塞狀態。
* 2.不同點:1)兩個方法聲明的位置不同:Thread類中聲明sleep() , Object類中聲明wait()
* 2)調用的要求不同:sleep()可以在任何需要的場景下調用。 wait()必須使用在同步代碼塊或同步方法中
* 3)關於是否釋放同步監視器:如果兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放鎖,wait()會釋放鎖。
package com.company;
// * 線程通信的例子:使用兩個線程打印 1-100。線程1, 線程2 交替打印
public class Comm {
    public static void main(String[] args) {
        CommThread th=new CommThread();
        Thread t1=new Thread(th,"線程1");
        Thread t2=new Thread(th,"線程2");

        t1.start();
        t2.start();
    }
}


class CommThread implements Runnable{
    private  int count=100;
    private  Object ob=new Object();
    @Override
    public void run() {
        while (true)
        {
           synchronized (ob)
           {
               ob.notify(); if(count>0)
               {
                   System.out.println("xincheng"+Thread.currentThread().getName()+":"+count);
                   count--;
                   try {
                       ob.wait();
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               else
               {
                   break;
               }
           }
        }
    }
}
* 解決線程安全問題的方式三:Lock鎖 --- JDK5.0新增
*
* 1. 面試題:synchronized 與 Lock的異同?
* 相同:二者都可以解決線程安全問題
* 不同:synchronized機制在執行完相應的同步代碼以后,自動的釋放同步監視器
* Lock需要手動的啟動同步(lock()),同時結束同步也需要手動的實現(unlock())
*
* 2.優先使用順序:
* Lock  同步代碼塊(已經進入了方法體,分配了相應資源)  同步方法(在方法體之外)
*
*
* 面試題:如何解決線程安全問題?有幾種方式

package com.company;

import java.util.concurrent.locks.ReentrantLock;

public class MainLock {
    public static void main(String[] args) {
        Test th=new Test();
        Thread t1=new Thread(th,"窗口1");
        Thread t2=new Thread(th,"窗口2");
        Thread t3=new Thread(th,"窗口3");

        t1.start();
        t2.start();
        t3.start();
    }


}


class  Test implements Runnable{
    private  int ticket=100;
    ////1.實例化ReentrantLock
    private ReentrantLock Lock=new ReentrantLock();
    @Override
    public void run() {
        while (true)
        {
            ////2.調用鎖定方法lock()
                Lock.lock();
            try {
                {
                    if(ticket>0){
                        System.out.println(Thread.currentThread().getName()+"買票:"+ticket);
                        ticket--;
                    }
                    else {
                        break;
                    }
                }
            } finally {
                // //3.調用解鎖方法:unlock()
 Lock.unlock();
            }

        }
    }
}
package com.company;
/* * 線程通信的應用:經典例題:生產者/消費者問題
         *
         * 生產者(Productor)將產品交給店員(Clerk),而消費者(Customer)從店員處取走產品,
         * 店員一次只能持有固定數量的產品(比如:20),如果生產者試圖生產更多的產品,店員
         * 會叫生產者停一下,如果店中有空位放產品了再通知生產者繼續生產;如果店中沒有產品
         * 了,店員會告訴消費者等一下,如果店中有產品了再通知消費者來取走產品。
         *
         * 分析:
         * 1. 是否是多線程問題?是,生產者線程,消費者線程
         * 2. 是否有共享數據?是,店員(或產品)
         * 3. 如何解決線程的安全問題?同步機制,有三種方法
         * 4. 是否涉及線程的通信?是*/
public class Product {
    public static void main(String[] args) {
        Clerk clerk=new Clerk();

        Producer p1=new Producer(clerk);
        Consumer c1=new Consumer(clerk);

        p1.start();
        c1.start();
    }
}

//共享數據可以認為是Clerk或者producecount
class Clerk{
    private int producecount=0;

    public synchronized void produceProduct(){
        if(producecount<20)
        {
            producecount++;
            System.out.println(Thread.currentThread().getName()+":生產"+producecount);

            notify();
        }
        else
        {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void comsumProduct(){
        if(producecount>0)
        {
            producecount--;
            System.out.println(Thread.currentThread().getName()+":消費"+producecount);

            notify();
        }
        else
        {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

//生產者線程
class  Producer extends  Thread{
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":開始生產產品.....");
        while (true)
        {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.produceProduct();
        }
    }
}

//消費者的線程
class  Consumer extends Thread{
    private  Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        System.out.println(getName() + ":開始消費產品.....");
        while (true)
        {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            clerk.comsumProduct();
        }
    }
}
* 創建線程的方式三:實現Callable接口。 --- JDK 5.0新增
*
*
* 如何理解實現Callable接口的方式創建多線程比實現Runnable接口創建多線程方式強大?
* 1. call()可以有返回值的。
* 2. call()可以拋出異常,被外面的操作捕獲,獲取異常的信息
* 3. Callable是支持泛型的
//1.創建一個實現Callable的實現類
class NumThread implements Callable{
    //2.實現call方法,將此線程需要執行的操作聲明在call()中
    @Override
    public Object call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if(i % 2 == 0){
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    public static void main(String[] args) {
        //3.創建Callable接口實現類的對象
        NumThread numThread = new NumThread();
        //4.將此Callable接口實現類的對象作為傳遞到FutureTask構造器中,創建FutureTask的對象
        FutureTask futureTask = new FutureTask(numThread);
        //5.將FutureTask的對象作為參數傳遞到Thread類的構造器中,創建Thread對象,並調用start()
        new Thread(futureTask).start();

        try {
            //6.獲取Callable中call方法的返回值
            //get()返回值即為FutureTask構造器參數Callable實現類重寫的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("總和為:" + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

}
* 創建線程的方式四:使用線程池
*
* 好處:
* 1.提高響應速度(減少了創建新線程的時間)
* 2.降低資源消耗(重復利用線程池中線程,不需要每次都創建)
* 3.便於線程管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大線程數
* keepAliveTime:線程沒有任務時最多保持多長時間后會終止

class NumberThread implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 == 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{

    @Override
    public void run() {
        for(int i = 0;i <= 100;i++){
            if(i % 2 != 0){
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {

    public static void main(String[] args) {
        //1. 提供指定線程數量的線程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //設置線程池的屬性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.執行指定的線程的操作。需要提供實現Runnable接口或Callable接口實現類的對象
        service.execute(new NumberThread());//適合適用於Runnable
        service.execute(new NumberThread1());//適合適用於Runnable

//        service.submit(Callable callable);//適合使用於Callable
        //3.關閉連接池
        service.shutdown();
    }

}

 

參考

 

互斥同步

synchronized 和 ReentrantLock。

互斥同步最主要的問題就是線程阻塞和喚醒所帶來的性能問題,因此這種同步也稱為阻塞同步。

互斥同步屬於一種悲觀的並發策略,總是認為只要不去做正確的同步措施,那就肯定會出現問題。無論共享數據是否真的會出現競爭,它都要進行加鎖(這里討論的是概念模型,實際上虛擬機會優化掉很大一部分不必要的加鎖)、用戶態核心態轉換、維護鎖計數器和檢查是否有被阻塞的線程需要喚醒等操作。

隨着硬件指令集的發展,我們可以使用基於沖突檢測的樂觀並發策略:先進行操作,如果沒有其它線程爭用共享數據,那操作就成功了,否則采取補償措施(不斷地重試,直到成功為止)。這種樂觀的並發策略的許多實現都不需要將線程阻塞,因此這種同步操作稱為非阻塞同步。

樂觀鎖需要操作和沖突檢測這兩個步驟具備原子性,這里就不能再使用互斥同步來保證了,只能靠硬件來完成。硬件支持的原子性操作最典型的是:比較並交換(Compare-and-Swap,CAS)。CAS 指令需要有 3 個操作數,分別是內存地址 V、舊的預期值 A 和新值 B。當執行操作時,只有當 V 的值等於 A,才將 V 的值更新為 B。

 

鎖優化

 

公平鎖/非公平鎖

 

公平鎖是指多個線程按照申請鎖的順序來獲取鎖。

非公平鎖是指多個線程獲取鎖的順序並不是按照申請鎖的順序,有可能后申請的線程比先申請的線程優先獲取鎖。有可能,會造成優先級反轉或者飢餓現象。

對於Java ReentrantLock而言,通過構造函數指定該鎖是否是公平鎖,默認是非公平鎖。非公平鎖的優點在於吞吐量比公平鎖大。

對於Synchronized而言,也是一種非公平鎖。由於其並不像ReentrantLock是通過AQS的來實現線程調度,所以並沒有任何辦法使其變成公平鎖。

獨享鎖/共享鎖

獨享鎖是指該鎖一次只能被一個線程所持有。

共享鎖是指該鎖可被多個線程所持有。

對於Java ReentrantLock而言,其是獨享鎖。但是對於Lock的另一個實現類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨享鎖。

讀鎖的共享鎖可保證並發讀是非常高效的,讀寫,寫讀 ,寫寫的過程是互斥的。

獨享鎖與共享鎖也是通過AQS來實現的,通過實現不同的方法,來實現獨享或者共享。

對於Synchronized而言,當然是獨享鎖。

互斥鎖/讀寫鎖

上面講的獨享鎖/共享鎖就是一種廣義的說法,互斥鎖/讀寫鎖就是具體的實現。

互斥鎖在Java中的具體實現就是ReentrantLock

讀寫鎖在Java中的具體實現就是ReadWriteLock

樂觀鎖/悲觀鎖

樂觀鎖與悲觀鎖不是指具體的什么類型的鎖,而是指看待並發同步的角度。

悲觀鎖認為對於同一個數據的並發操作,一定是會發生修改的,哪怕沒有修改,也會認為修改。因此對於同一個數據的並發操作,悲觀鎖采取加鎖的形式。悲觀的認為,不加鎖的並發操作一定會出問題。

樂觀鎖則認為對於同一個數據的並發操作,是不會發生修改的。在更新數據的時候,會采用嘗試更新,不斷重新的方式更新數據。樂觀的認為,不加鎖的並發操作是沒有事情的。

從上面的描述我們可以看出,悲觀鎖適合寫操作非常多的場景,樂觀鎖適合讀操作非常多的場景,不加鎖會帶來大量的性能提升。

悲觀鎖在Java中的使用,就是利用各種鎖。

樂觀鎖在Java中的使用,是無鎖編程,常常采用的是CAS算法,典型的例子就是原子類,通過CAS自旋實現原子操作的更新。

分段鎖

分段鎖其實是一種鎖的設計,並不是具體的一種鎖,對於ConcurrentHashMap而言,其並發的實現就是通過分段鎖的形式來實現高效的並發操作。

我們以ConcurrentHashMap來說一下分段鎖的含義以及設計思想,ConcurrentHashMap中的分段鎖稱為Segment,它即類似於HashMap(JDK7與JDK8中HashMap的實現)的結構,即內部擁有一個Entry數組,數組中的每個元素又是一個鏈表;同時又是一個ReentrantLock(Segment繼承了ReentrantLock)。

當需要put元素的時候,並不是對整個hashmap進行加鎖,而是先通過hashcode來知道他要放在那一個分段中,然后對這個分段進行加鎖,所以當多線程put的時候,只要不是放在一個分段中,就實現了真正的並行的插入。

但是,在統計size的時候,可就是獲取hashmap全局信息的時候,就需要獲取所有的分段鎖才能統計。

分段鎖的設計目的是細化鎖的粒度,當操作不需要更新整個數組的時候,就僅僅針對數組中的一項進行加鎖操作。

偏向鎖/輕量級鎖/重量級鎖

這三種鎖是指鎖的狀態,並且是針對Synchronized。在Java 5通過引入鎖升級的機制來實現高效Synchronized。這三種鎖的狀態是通過對象監視器在對象頭中的字段來表明的。

偏向鎖是指一段同步代碼一直被一個線程所訪問,那么該線程會自動獲取鎖。降低獲取鎖的代價。

輕量級鎖是指當鎖是偏向鎖的時候,被另一個線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。

重量級鎖是指當鎖為輕量級鎖的時候,另一個線程雖然是自旋,但自旋不會一直持續下去,當自旋一定次數的時候,還沒有獲取到鎖,就會進入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓其他申請的線程進入阻塞,性能降低。

 

自旋鎖

互斥同步進入阻塞狀態的開銷都很大,應該盡量避免。在許多應用中,共享數據的鎖定狀態只會持續很短的一段時間。自旋鎖的思想是讓一個線程在請求一個共享數據的鎖時執行忙循環(自旋)一段時間,如果在這段時間內能獲得鎖,就可以避免進入阻塞狀態。

自旋鎖雖然能避免進入阻塞狀態從而減少開銷,但是它需要進行忙循環操作占用 CPU 時間,它只適用於共享數據的鎖定狀態很短的場景。

在 JDK 1.6 中引入了自適應的自旋鎖。自適應意味着自旋的次數不再固定了,而是由前一次在同一個鎖上的自旋次數及鎖的擁有者的狀態來決定。

鎖消除

鎖消除是指對於被檢測出不可能存在競爭的共享數據的鎖進行消除。

鎖消除主要是通過逃逸分析來支持,如果堆上的共享數據不可能逃逸出去被其它線程訪問到,那么就可以把它們當成私有數據對待,也就可以將它們的鎖進行消除。

 

鎖粗化

如果一系列的連續操作都對同一個對象反復加鎖和解鎖,頻繁的加鎖操作就會導致性能損耗。

如果虛擬機探測到由這樣的一串零碎的操作都對同一個對象加鎖,將會把加鎖的范圍擴展(粗化)到整個操作序列的外部。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM