一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

深入學習synchronized

 小仙女本仙人 2021-07-04
synchronized

并發(fā)編程中的三個問題:

可見性(Visibility)

是指一個線程對共享變量進行修改,另一個先立即得到修改后的最新值。

代碼演示:

public class Test01Visibility {
    public static boolean flag = true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (flag) {

            }
        }).start();

        Thread.sleep(2000);

        new Thread(() -> {
            flag = false;
            System.out.println("修改了flag");
        }).start();
    }
}

小結(jié):并發(fā)編程時,會出現(xiàn)可見性問題,當一個線程對共享變量進行了修改,另外的線程并沒有立即看到修改

后的最新值。

原子性(Atomicity)

在一次或多次操作中,要么所有的操作都執(zhí)行并且不會受其他因素干擾而中斷,要么所有的操作都不執(zhí)行

代碼演示:

public class Test02Atomicity {
    public static int num = 0;
    public static void main(String[] args) throws InterruptedException {
        // 創(chuàng)建任務(wù)
        Runnable increment = () -> {
            for (int i = 0; i < 1000; i++) {
                num++;
            }
        };
        ArrayList<Thread> threads = new ArrayList<>();
        //創(chuàng)建線程
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(increment);
            t.start();
            threads.add(t);
        }
        for (Thread thread : threads) {
            thread.join();
        }
        System.out.println(num);
    }
}

通過 javap -p -v Test02Atomicity對class 文件進行反匯編:發(fā)現(xiàn)++ 操作是由4條字節(jié)碼指令組成,并不是原子操作

小結(jié):并發(fā)編程時,會出現(xiàn)原子性問題,當一個線程對共享變量操作到一半時,另外的線程也有可能來操作共享變量,干擾了前一個線程的操作

有序性(Ordering)

是指程序中代碼的執(zhí)行順序,Java在編譯時和運行時會對代碼進行優(yōu)化,會導致程序最終的執(zhí)行順序不一定就是我們編寫代碼時的順序。

代碼演示:

@JCStressTest
@Outcome(id={"1","4"},expect=Expect.ACCEPTABLE,desc="ok")
@Outcome(id="0",expect=Expect.ACCEPTABLE_INTERESTING,desc="danger")
@State
public class Test03Orderliness { 
    int num=0;
    boolean ready=false;
    //線程一執(zhí)行的代碼
    @Actor
    public void actor1(I_Resultr){
        if(ready){
            r.r1=num+num;
        }else{
            r.r1=1;
        }
    }
    //線程2執(zhí)行的代碼
    @Actor
    public void actor2(I_Resultr){
        num=2;
        ready=true;
    }
}

運行的結(jié)果有:0、1、4

小結(jié):程序代碼在執(zhí)行過程中的先后順序,由于Java在編譯期以及運行期的優(yōu)化,導致了代碼的執(zhí)行順序未必

就是開發(fā)者編寫代碼時的順序。

Java內(nèi)存模型(JMM)

計算機結(jié)構(gòu)簡介

根據(jù)馮諾依曼體系結(jié)構(gòu),計算機由五大組成部分,輸入設(shè)備,輸出設(shè)備,存儲器,控制器,運算器。

CPU:

中央處理器,是計算機的控制和運算的核心,我們的程序最終都會變成指令讓CPU去執(zhí)行,處理程序中的數(shù)據(jù)。

內(nèi)存:

我們的程序都是在內(nèi)存中運行的,內(nèi)存會保存程序運行時的數(shù)據(jù),供CPU處理。

緩存:

CPU的運算速度和內(nèi)存的訪問速度相差比較大。這就導致CPU每次操作內(nèi)存都要耗費很多等待時間。于是就有了在

CPU和主內(nèi)存之間增加緩存的設(shè)計。CPU Cache分成了三個級別: L1, L2, L3。級別越小越接近CPU,速度也更快,同時也代表著容量越小。

Java內(nèi)存模型

Java內(nèi)存模型是一套規(guī)范,描述了Java程序中各種變量(線程共享變量)的訪問規(guī)則,以及在JVM中將變量存儲到內(nèi)存和從內(nèi)存中讀取變量這樣的底層細節(jié),具體如下。

主內(nèi)存

主內(nèi)存是所有線程都共享的,都能訪問的。所有的共享變量都存儲于主內(nèi)存。

工作內(nèi)存

每一個線程有自己的工作內(nèi)存,工作內(nèi)存只存儲該線程對共享變量的副本。線程對變量的所有的操作(讀,取)都必須在工作內(nèi)存中完成,而不能直接讀寫主內(nèi)存中的變量,不同線程之間也不能直接訪問對方工作內(nèi)存中的變量。

小結(jié)

Java內(nèi)存模型是一套規(guī)范,描述了Java程序中各種變量(線程共享變量)的訪問規(guī)則,以及在JVM中將變量存儲到內(nèi)存和從內(nèi)存中讀取變量這樣的底層細節(jié),Java內(nèi)存模型是對共享數(shù)據(jù)的可見性、有序性、和原子性的規(guī)則和保障。

主內(nèi)存與工作內(nèi)存之間的交互

注意:1. 如果對一個變量執(zhí)行l(wèi)ock操作,將會清空工作內(nèi)存中此變量的值

  1. 對一個變量執(zhí)行unlock操作之前,必須先把此變量同步到主內(nèi)存中

synchronized保證三大特性

synchronized保證可見性

while(flag){
    //增加對象共享數(shù)據(jù)的打印,println是同步方法
    System.out.println("run="+run);
}

小結(jié):

synchronized保證可見性的原理,執(zhí)行synchronized時,lock原子操作會刷新工作內(nèi)存中共享變量的值。

synchronized保證原子性

for(int i = 0; i < 1000; i++){
    synchronized(Test01Atomicity.class){
        number++;
    }
}

小結(jié):

synchronized保證原子性的原理,synchronized保證只有一個線程拿到鎖,能夠進入同步代碼塊。

synchronized保證有序性

synchronized(Test01Atomicity.class){
    num=2;
ready=true;
}

小結(jié)

synchronized保證有序性的原理,我們加synchronized后,依然會發(fā)生重排序,只不過,我們有同步代碼塊,可以保證只有一個線程執(zhí)行同步代碼中的代碼,保證有序性。

synchronized的特性

可重入特性

public class Demo01 {
    public static void main(String[] args) {
        new MyThread().start();
        new MyThread().start();
    }
}
class MyThread extends Thread {
    @Override
    public void run() {
        synchronized (MyThread.class) {
            System.out.println(Thread.currentThread().getName() + "獲取了鎖1");
            synchronized (MyThread.class) {
                System.out.println(Thread.currentThread().getName() + "獲取了鎖2");
            }
        }
    }
}

可重入原理:

synchronized的鎖對象中有一個計數(shù)器(recursions變量)會記錄線程獲得幾次鎖。

可重入的好處:

  1. 可以避免死鎖

  2. 可以讓我們更好的來封裝代碼

小結(jié):

synchronized是可重入鎖,內(nèi)部鎖對象中會有一個計數(shù)器記錄線程獲取幾次鎖啦,獲取一次鎖加+1,在執(zhí)行完同步代碼塊時,計數(shù)器的數(shù)量會-1,直到計數(shù)器的數(shù)量為0,就釋放這個鎖。

不可中斷特性

什么是不可中斷?

一個線程獲得鎖后,另一個線程想要獲得鎖,必須處于阻塞或等待狀態(tài),如果第一個線程不釋放鎖,第二個線程會一直阻塞或等待,不可被中斷。

public class Uninterruptible {
    private static Object obj = new Object();
    public static void main(String[] args) throws InterruptedException {
        Runnable run = () -> {
            synchronized (obj) {
                String name = Thread.currentThread().getName();
                System.out.println(name + "執(zhí)行同步代碼塊");
                try {
                    Thread.sleep(888888);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread t1 = new Thread(run);
        t1.start();
        Thread.sleep(1000);
        Thread t2 = new Thread(run);
        t2.start();

        Thread.sleep(1000);
        System.out.println("停止線程2前");
        System.out.println(t2.getState());
        t2.interrupt();
        System.out.println("停止線程2后");
        System.out.println(t1.getState());
        System.out.println(t2.getState());
    }
}

synchronized是不可中斷,處于阻塞狀態(tài)的線程會一直等待鎖。

ReentrantLock可中斷演示

public class Interruptible {
    private static Lock lock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        test01();
    }

    private static void test01() throws InterruptedException {
        Runnable run = () -> {
            boolean flag = false;
            String  name = Thread.currentThread().getName();
            try {
                flag = lock.tryLock(3, TimeUnit.SECONDS);
                if (flag) {
                    System.out.println(name + "獲得鎖,進入鎖執(zhí)行");
                    Thread.sleep(888888);
                } else {
                    System.out.println(name + "沒有獲得鎖");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                if (flag) {
                    lock.unlock();
                    System.out.println(name + "釋放鎖");
                }
            }
        };

        Thread t1 = new Thread(run);
        t1.start();
        Thread.sleep(1000);

        Thread t2 = new Thread(run);
        t2.start();
    }
}

小結(jié):

synchronized屬于不可被中斷

Lock的lock方法是不可中斷的

Lock的tryLock方法是可中斷的

synchronized 的原理

monitorenter:

每一個對象都會和一個監(jiān)視器monitor關(guān)聯(lián)。監(jiān)視器被占用時會被鎖住,其他線程無法來獲取該monitor。當JVM執(zhí)行某個線程的某個方法內(nèi)部的monitorenter時,它會嘗試去獲取當前對象對應(yīng)的monitor的所有權(quán)。其過程如下:

  1. 若monior的進入數(shù)為0,線程可以進入monitor,并將monitor的進入數(shù)置為1。當前線程成為monitor的owner(所有者)

  2. 若線程已擁有monitor的所有權(quán),允許它重入monitor,則進入monitor的進入數(shù)加1

  3. 若其他線程已經(jīng)占有monitor的所有權(quán),那么當前嘗試獲取monitor的所有權(quán)的線程會被阻塞,直到monitor的進入數(shù)變?yōu)?,才能重新嘗試獲取monitor的所有權(quán)。

monitorenter小結(jié):

synchronized的鎖對象會關(guān)聯(lián)一個monitor, 這個monitor不是我們主動創(chuàng)建的, 是JVM的線程執(zhí)行到這個同步代碼塊,發(fā)現(xiàn)鎖對象

有monitor就會創(chuàng)建monitor, monitor內(nèi)部有兩個重要的成員變量owner擁有這把鎖的線程,recursions會記錄線程擁有鎖的次數(shù),

當一個線程擁有monitor后其他線程只能等待。

monitorexit:

  1. 能執(zhí)行monitorexit 指令的線程一定是擁有當前對象的monitor的所有權(quán)的線程。

  2. 執(zhí)行monitorexit 時會將monitor的進入數(shù)減1。當monitor的進入數(shù)減為0時,當前線程退出monitor,不再擁有monitor的所有權(quán),此時其他被這個monitor阻塞的線程可以嘗試去獲取這個monitor的所有權(quán)

monitorexit釋放鎖。

monitorexit插入在方法結(jié)束處和異常處,JVM保證每個monitorenter必須有對應(yīng)的monitorexit。

面試題synchroznied出現(xiàn)異常會釋放鎖嗎?

:會釋放鎖。

同步方法

同步方法在反匯編后,會增加ACC_SYNCHRONIZED修飾。會隱式調(diào)用monitorenter 和monitorexit。在執(zhí)行同步方法前會調(diào)用

monitorenter,在執(zhí)行完同步方法后會調(diào)用monitorexit 。

小結(jié):

通過javap反匯編可以看到synchronized 使用了monitorentor和monitorexit兩個指令。每個鎖對象都會關(guān)聯(lián)一個monitor(監(jiān)視

器,它才是真正的鎖對象),它內(nèi)部有兩個重要的成員變量owner會保存獲得鎖的線程,recursions會保存線程獲得鎖的次數(shù), 當執(zhí)行到

monitorexit時, recursions會-1, 當計數(shù)器減到0時這個線程就會釋放鎖。

面試題:synchronized與Lock的區(qū)別

1、synchronized 是關(guān)鍵字,lock 是一個接口

2、synchronized 會自動釋放鎖,lock 需要手動釋放鎖。

3、synchronized 是不可中斷的,lock 可以中斷也可以不中斷。

4、通過lock 可以知道線程有沒有拿到鎖,而synchronized 不能。

5、synchronized 能鎖住方法和代碼塊,而lock 只能鎖住代碼塊。

6、lock 可以使用讀鎖提高多線程讀效率。

7、synchronized 是非公平鎖,ReentrantLock 可以控制是否是公平鎖。

CAS

cas的概述和作用:

compare and swap,可以將比較和交換轉(zhuǎn)為原子操作,這個原子操作直接由cpu保證,cas可以保證共享變量賦值時的原子操作,cas依賴3個值:內(nèi)存中的值v,舊的預估值x,要修改的新值b。根據(jù)atomicInteger的地址加上偏移量offset的值可以得到內(nèi)存中的值,將內(nèi)存中的值和舊的預估值進行比較,如果相同,就將新值保存到內(nèi)存中。不相同就進行重試。

Java對象的布局

在JVM中,對象在內(nèi)存中的布局分為三塊區(qū)域:對象頭、實例數(shù)據(jù)和對齊填充。如下圖所示:

HotSpot采用instanceOopDesc和arrayOopDesc來描述對象頭,arrayOopDesc對象用來描述數(shù)組類型。

從instanceOopDesc代碼中可以看到 instanceOopDesc繼承自oopDesc。

_mark表示對象標記、屬于markOop類型,也就是Mark World,它記錄了對象和鎖有關(guān)的信息

_metadata表示類元信息,類元信息存儲的是對象指向它的類元數(shù)據(jù)(Klass)的首地址,其中Klass表示普通指針、compressed_klass表示壓縮類指針。

Mark Word

鎖狀態(tài) 存儲內(nèi)容 鎖標志位
無鎖 對象的hashcode、對象分代年齡、是否是偏向鎖(0) 01
偏向鎖 偏向線程id、偏向時間戳、對象分代年齡、是否是偏向鎖(1) 01
輕量級鎖 指向棧中鎖記錄的指針 00
重量級鎖 指向互斥量(重量級鎖)的指針 10

klass pointer

用于存儲對象的類型指針,該指針指向它的類元數(shù)據(jù),JVM通過這個指針確定對象是哪個類的實例。通過-XX:+UseCompressedOops開啟指針壓縮,

在64位系統(tǒng)中,Mark Word = 8 bytes,類型指針 = 8bytes,對象頭 = 16 bytes = 128bits;

實例數(shù)據(jù)

就是類中定義的成員變量。

對齊填充

由于HotSpot VM的自動內(nèi)存管理系統(tǒng)要求對象起始地址必須是8字節(jié)的整數(shù)倍,對象的大小必須是8字節(jié)的整數(shù)倍。因此,當對象實例數(shù)據(jù)部分沒有對齊時,就需要通過對齊填來補全。

查看Java對象布局

<dependency>
   <groupId>org.openjdk.jol</groupId>
   <artifactId>jol-core</artifactId>
   <version>0.9</version>
</dependency>

小結(jié)

Java對象由3部分組成,對象頭,實例數(shù)據(jù),對齊數(shù)據(jù),對象頭分成兩部分:Mark World + Klass pointer

偏向鎖

什么是偏向鎖?

鎖會偏向于第一個獲得它的線程,會在對象頭存儲鎖偏向的線程ID,以后該線程進入和退出同步塊時只需要檢查是否為偏向鎖、鎖標志位以及ThreadID即可。

不過一旦出現(xiàn)多個線程競爭時必須撤銷偏向鎖,所以撤銷偏向鎖消耗的性能必須小于之前節(jié)省下來的CAS原子操作的性能消耗,不然就得不償失了。

偏向鎖原理

當線程第一次訪問同步塊并獲取鎖時,偏向鎖處理流程如下:

  1. 虛擬機將會把對象頭中的標志位設(shè)為“01”,即偏向模式。
  2. 同時使用CAS操作把獲取到這個鎖的線程的ID記錄在對象的Mark Word之中,如果CAS操作成功,持有偏向鎖的線程以后每次進入這個鎖相關(guān)的同步塊時,虛擬機都可以不再進行任何同步操作,偏向鎖的效率高。

偏向鎖的撤銷

  1. 偏向鎖的撤銷動作必須等待全局安全點

  2. 暫停擁有偏向鎖的線程,判斷鎖對象是否處于被鎖定狀態(tài)

  3. 撤銷偏向鎖,恢復到無鎖(標志位為01)或輕量級鎖(標志位為00)的狀態(tài)

偏向鎖是自適應(yīng)的

小結(jié):

偏向鎖的原理是什么?

當鎖對象第一次被線程獲取的時候,虛擬機將會把對象頭中的標志位設(shè)為“01”,即偏向模式。同時使用CAS操作把獲取到這個鎖的線程的ID記錄在對象的MarkWord之中,如果CAS操作成功,持有偏向鎖的線程以后每次進入這個鎖相關(guān)的同步塊時,虛擬機都可以不再進行任何同步操作,偏向鎖的效率高。

偏向鎖的好處是什么?

偏向鎖是在只有一個線程執(zhí)行同步塊時進一步提高性能,適用于一個線程反復獲得同一鎖的情況。偏向鎖可以提高帶有同步但無競爭的程序性能。

輕量級鎖

什么是輕量級鎖?

輕量級鎖是JDK 6之中加入的新型鎖機制,輕量級鎖并不是用來代替重量級鎖的。

引入輕量級鎖的目的:在多線程交替執(zhí)行同步塊的情況下,盡量避免重量級鎖引起的性能消耗,但是如果多個線程在同一時刻進入臨界區(qū),會導致輕量級鎖膨脹升級為重量級鎖,所以輕量級鎖的出現(xiàn)并非是要代替重量級鎖。

輕量級鎖原理

當關(guān)閉偏向鎖或多個線程競爭偏向鎖導致偏向鎖升級為輕量級鎖,則會嘗試獲取輕量級鎖,其步驟如下:

  1. 判斷當前對象是否處于無鎖狀態(tài),如果是,則JVM 首先將在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word 的拷貝,將對象的Mark Word 復制到棧幀中的Lock Record 中,將Lock Record中的owner指向當前對象。
  2. JVM 利用CAS 操作嘗試將對象的Mark Word 更新為指向Lock Record 的指針,如果成功表示競爭到鎖,則將鎖標志位變成00,執(zhí)行同步操作。
  3. 如果失敗則判斷當前對象的Mark Word 是否指向當前線程的棧幀,如果是則表示當前線程已經(jīng)持有當前對象的鎖,則直接執(zhí)行同步代碼塊;否則只能說明該鎖對象已經(jīng)被其他線程搶占了,這時輕量級鎖需要膨脹為重量級鎖,鎖標志位變成10,后面等待的線程將會進入阻塞狀態(tài)。

輕量級鎖的釋放:

輕量級鎖的釋放也是通過CAS操作來進行的,主要步驟如下:

  1. 取出在獲取輕量級鎖時保存在Mark Word 中的數(shù)據(jù);
  2. 用CAS 操作將取出的數(shù)據(jù)替換當前對象的Mark Word 中,如果成功,則說明釋放鎖成功。
  3. 如果CAS 操作替換失敗,說明有其他線程獲取該鎖,則需要將輕量級鎖膨脹升級為重量級鎖。

對于輕量級鎖,其性能提升的依據(jù)是“對于絕大部分的鎖,在整個生命周期內(nèi)都是不會存在競爭的”,如果打破這個依據(jù)則除了互斥的開銷外,還有額外的CAS 操作,因此在有多線程競爭的情況下,輕量級鎖比重量級鎖更慢。

輕量級鎖好處:

在多線程交替執(zhí)行同步塊的情況下,可以避免重量級鎖引起的性能消耗。

自旋鎖

monitor 實現(xiàn)鎖的時候, monitor 會阻塞和喚醒線程,線程的阻塞和喚醒需要CPU 從用戶態(tài)轉(zhuǎn)為核心態(tài),頻繁的阻塞和喚醒對CPU 來說是一件負擔很重的工作,這些操作給系統(tǒng)的并發(fā)性能帶來了很大的壓力。同時,共享數(shù)據(jù)的鎖定狀態(tài)可能只會持續(xù)很短的一段時間,為了這段時間阻塞和喚醒線程并不值得。如果有一個以上的處理器,能讓兩個或以上的線程同時并行執(zhí)行,就可以讓后面請求鎖的那個線程“稍微等一下”,但不放棄處理器的執(zhí)行時間,看看持有鎖的線程是否釋放了鎖。為了讓線程等待,我們只需讓線程執(zhí)行一個循環(huán)(即自旋),這就是自旋鎖。

自旋鎖在JDK 1.4.2中就已經(jīng)引入,只不過默認是關(guān)閉的,可以使用-XX:+UseSpinning參數(shù)來開啟,在JDK 6中就已經(jīng)改為默認開啟了。自旋等待不能代替阻塞,且先不說對處理器數(shù)量的要求,自旋等待本身雖然避免了線程切換的開銷,但它是要占用處理器時間的,因此,如果鎖被占用的時間很短,自旋等待的效果就會非常好,反之,如果鎖被占用的時間很長。那么自旋的線程只會白白消耗處理器資源,而不會做任何有用的工作,反而會帶來性能上的浪費。因此,自旋等待的時間必須要有一定的限度,如果自旋超過了限定的次數(shù)仍然沒有成功獲得鎖,就應(yīng)當使用傳統(tǒng)的方式去掛起線程了。自旋次數(shù)的默認值是10次,用戶可以使用參數(shù)-XX : PreBlockSpin來更改。

適應(yīng)性自旋鎖

在JDK 6 中引入了自適應(yīng)的自旋鎖。如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運行中,那么虛擬機就會認為這次自旋也很有可能再次成功,進而它將允許自旋等待持續(xù)相對更長的時間。如果對于某個鎖,自旋很少成功獲得過,那在以后要獲取這個鎖時將可能省略掉自旋過程,以避免浪費處理器資源。

平時寫代碼如何對synchronized優(yōu)化

減少synchronized的范圍:

同步代碼塊中盡量短,減少同步代碼塊中代碼的執(zhí)行時間,減少鎖的競爭。

synchronized(Demo01.class){
    System.out.println("aaa");
}

降低synchronized鎖的粒度:

將一個鎖拆分為多個鎖提高并發(fā)度,如HashTable:鎖定整個哈希表,一個操作正在進行時,其他操作也同時鎖定,效率低下。ConcurrentHashMap:局部鎖定,只鎖定桶。

讀寫分離:

讀取時不加鎖,寫入和刪除時加鎖

ConcurrentHashMap,CopyOnWriteArrayList和ConyOnWriteSet

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美二区视频在线观看| 国产亚洲系列91精品| 国产在线一区中文字幕| 在线免费视频你懂的观看| 不卡视频免费一区二区三区| 国产亚洲欧美日韩国亚语| 九九热九九热九九热九九热| 国产免费黄片一区二区| 麻豆国产精品一区二区三区| 欧美一区二区三区视频区| 精品推荐国产麻豆剧传媒| 国产亚洲午夜高清国产拍精品| 欧美午夜不卡在线观看| 黑丝袜美女老师的小逼逼| 久久精品免费视看国产成人| 天海翼精品久久中文字幕| 冬爱琴音一区二区中文字幕| 亚洲精品熟女国产多毛 | 99久久国产综合精品二区| 欧美丰满人妻少妇精品| 亚洲欧美日本国产不卡| 日本高清不卡一二三区| 91一区国产中文字幕| 欧美日韩国产福利在线观看| 久久天堂夜夜一本婷婷| 色丁香之五月婷婷开心| 国产精品自拍杆香蕉视频| 中文字幕高清不卡一区| 国产av一二三区在线观看| 色婷婷在线视频免费播放| 日韩国产中文在线视频| 色婷婷视频在线精品免费观看| 国产欧美日韩不卡在线视频| 日韩精品小视频在线观看| 婷婷九月在线中文字幕| 国产精品熟女乱色一区二区| 黄色国产自拍在线观看| 男女一进一出午夜视频| 欧美日韩亚洲巨色人妻| 毛片在线观看免费日韩| 老司机激情五月天在线不卡|