成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳盡探究synchronized原理后,原來性能這么好

開發(fā) 前端
在使用synchronized的時候一定要注意hashcode生成對鎖的影響,因為對象頭的mark word是保存對象運(yùn)行期數(shù)據(jù)的,這塊區(qū)域在32位機(jī)器上是32字節(jié),在64位機(jī)器上是64字節(jié),所以空間是有限的,是無法同時保存hashcode和輕量級鎖記錄指針或者M(jìn)onitor對象指針的,因為會做一些取舍。

前置思考

我們先來試著想一下實現(xiàn)一把鎖應(yīng)該考慮哪些問題

  1. 如何獲取鎖資源?
  2. 獲取不到鎖資源的線程如何處理?
  3. 如何釋放鎖資源?
  4. 資源釋放后如何讓其他線程再次獲取鎖資源?

帶著上面幾個問題更深層次的思考如何解決上面幾個問題

  • 鎖的標(biāo)識
    需要有個標(biāo)識或者狀態(tài)來表示鎖是否已經(jīng)被占用。
  • 線程搶鎖的邏輯
    多個線程如何搶鎖,如何才算搶到了鎖。
  • 線程掛起的邏輯
    線程如果沒有搶到鎖,我們都知道線程會阻塞掛起,那么如何阻塞掛起呢。
  • 線程存儲機(jī)制
    線程在Java中的表現(xiàn)其實就是一個Java對象綁定內(nèi)核中的一條線程,也可以理解為這個對象是底層線程的載體,沒有搶到鎖的線程會阻塞掛起,那么線程的載體如何處置呢。一般我們想到的是將這些載體保存到集合或者隊列中。
  • 線程釋放鎖的邏輯
    線程在執(zhí)行完業(yè)務(wù)邏輯后就要釋放鎖,如何才算釋放了鎖呢?
  • 線程喚醒的邏輯
    鎖釋放后需要做什么呢,當(dāng)然是喚醒被阻塞的線程。

不管是哪一種鎖我認(rèn)為實現(xiàn)上必須都要考慮以上問題,而鎖的性能好壞就在于對以上問題解決的思路上是否為最優(yōu)的處理

synchronized的使用

在介紹原理前我們先介紹synchronized的使用

  1. synchronized可以修飾實例方法。
  2. synchronized可以修飾靜態(tài)方法。
  3. synchronized可以修飾實例方法的代碼塊。
  4. synchronized可以修飾靜態(tài)方法的代碼塊。
public void get(){
  synchronized(this){

  }
}
public synchronized void get(){
  
}

可見其使用是很簡單的,只需要一個關(guān)鍵字,不需要程序員關(guān)心什么時候上鎖,什么時候釋放鎖。

對象的生成

創(chuàng)建對象

想要了解synchronized的底層原理就必須先了解Java中的對象是怎么生成的,這對于掌握synchronized原理至關(guān)重要。

java中如何創(chuàng)建一個對象?java代碼會被編譯成字節(jié)碼然后被jvm運(yùn)行,jvm在遇到new關(guān)鍵字的時候就會啟動對象的創(chuàng)建流程,對象的大致流程如下:

圖片圖片

默認(rèn)情況下jvm加載類是懶加載的,所以創(chuàng)建對象的第一步是判斷類是否已經(jīng)加載,如果沒有加載,需要先走類的加載流程。

接下來是分配內(nèi)存,一個對象在類加載的時候就可以知道所需要的內(nèi)存大小,此時就是在堆中劃分一塊區(qū)域出來作為對象的私密空間,具體如何分配和具體使用的垃圾回收器有關(guān),jvm篇再細(xì)講,在偌大的堆中怎么為一個對象劃分區(qū)間呢?這里的分配主要是兩種方法:指針碰撞和空閑列表,但是不管哪種劃分方法都會存在并發(fā)問題,此時jvm的解決方案是TLAB和cas配合失敗重試,這些內(nèi)容也將會在jvm篇進(jìn)行了解,這里只需要知道給對象分配了內(nèi)存即可。

初始化零值這一步是給對象中的屬性賦零值,比如int類型默認(rèn)為0,這一步是避免屬性不賦值的情況下出現(xiàn)空指針異常。

每個對象都會有一個對象頭區(qū)域,這個區(qū)域包括Mark Word,元數(shù)據(jù)指針,數(shù)組長度三個部分,Mark Word用于保存對象的運(yùn)行時數(shù)據(jù),比如hashcode,分代年齡,鎖標(biāo)識等,元數(shù)據(jù)指針是當(dāng)前對象所屬類對象的地址,只有數(shù)組對象才會有數(shù)組長度。

最后初始化對象,這個時候一個完整的對象生成了。

一個完整對象的結(jié)構(gòu)如下:

圖片圖片

可以看到結(jié)構(gòu)中有一個對其填充,對其填充是為了滿足對象的大小為8字節(jié)的整數(shù)倍,只有8字節(jié)的整數(shù)倍才是最高效的存取方式。所以一個對象的大小總是8字節(jié)的整數(shù)倍。

對象頭

對象頭是對象中用于保存實例數(shù)據(jù)外的運(yùn)行時數(shù)據(jù)的區(qū)域。

我們知道java是面向?qū)ο蟮?,在java的世界一切皆對象,所以整個jvm的設(shè)計都是圍繞對象,包括對象所屬類的加載,對象的創(chuàng)建,對象的保存,對象的銷毀,對象的回收,鎖的實現(xiàn),以及jvm的內(nèi)存結(jié)構(gòu)等等都要圍繞對象設(shè)計,這就導(dǎo)致對象自身會有很多的運(yùn)行時數(shù)據(jù),比如垃圾回收依賴的分代年齡,代碼運(yùn)行過程中用于標(biāo)識對象唯一的hashcode,當(dāng)用作鎖對象的時候鎖的相關(guān)信息存儲,記錄當(dāng)前對象所屬的類對象指針等等。

所以jvm設(shè)計了對象頭,對象頭包括Mark Word,MetaDate,數(shù)組長度三部分。

Monitor

Java中的對象與生俱來會攜帶一個Monitor對象,這個Monitor對象可以說是對象的影子,它平時沒什么用,當(dāng)對象作為鎖資源被線程搶占的時候,它就排上用場了,可以說Monitor對象就是為實現(xiàn)鎖而發(fā)明的,Monitor就類似一個監(jiān)視器,所以說Java的老派鎖是監(jiān)視器鎖。

這個Monitor對象是jvm級別實現(xiàn)的,是一個jvm級別的對象,所以我們在java端開發(fā)的時候是看不到摸不到的,但卻是真實存在的。

Monitor對象結(jié)構(gòu)如下:

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; // 記錄個數(shù)
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL; // 占用資源的線程
    _WaitSet      = NULL; // 處于wait狀態(tài)的線程,會被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; // 處于等待鎖block狀態(tài)的線程,會被加入到該列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

實現(xiàn)原理

synchronized鎖實現(xiàn)就是依賴這個Monitor對象實現(xiàn)的,我們看到上面Monitor對象的結(jié)構(gòu)中的幾個屬性:_count,_owner,_WaitSet,_EntryList,大概也能猜的出來的這些參數(shù)都跟線程搶鎖有關(guān)系。

好了,我們直接來看這個鎖的實現(xiàn)原理。

synchronized鎖的寫法很簡單,簡單到只有一個關(guān)鍵字,我們也知道Java代碼是要編譯成字節(jié)碼文件然后被jvm虛擬機(jī)解析運(yùn)行的,那就不難猜出這把鎖的邏輯實現(xiàn)是在編譯的字節(jié)碼中。

synchronized關(guān)鍵字被編譯后的大致表現(xiàn)如下:

圖片圖片

整個代碼塊前后被monitorenter 和 monitorexit包裹,也就是說synchronized關(guān)鍵字的在編譯后就變?yōu)樯厦鎯蓚€關(guān)鍵字。到這里其實就和ReentrantLock的lock和unlock是一個道理了。

monitorenter 和 monitorexit這兩個是jvm級別字節(jié)碼指令,不難想到j(luò)vm在運(yùn)行代碼的時候,遇到monitorenter關(guān)鍵字,一定會啟動搶鎖的邏輯,包括搶鎖,入隊,阻塞;而遇到monitorexit的時候一定會走釋放鎖邏輯,包括釋放鎖,喚醒阻塞線程。

而所謂的入隊?wèi)?yīng)該就可以聯(lián)想到Monitor對象中的那幾個屬性了。阻塞的實現(xiàn)則是依賴于操作系統(tǒng)底層的互斥原語mutex。

上面做了一些相關(guān)知識的介紹,可能還比較碎片化,接下來我們就通過加鎖流程將所有信息都串聯(lián)起來。

Java1.5的鎖

java最開始的鎖的實現(xiàn)依賴鎖對象,對象的Monitor對象,操作系統(tǒng)互斥原語mutex。

  • 線程運(yùn)行到 monitorenter 關(guān)鍵字后,jvm判斷此線程進(jìn)入了互斥區(qū)域,jvm底層會調(diào)用操作系統(tǒng)底層的互斥原語Mutex實現(xiàn)線程線程互斥。
  • 如果在此之前沒有其他線程占據(jù)互斥區(qū)域,則當(dāng)前線程會占據(jù)互斥區(qū)域,意味著當(dāng)前線程搶到了鎖資源,會將自身的線程id存儲到鎖對象對應(yīng)的Monitor對象的_owner屬性,count屬性加1。
  • 如果此時互斥區(qū)域已經(jīng)有一個線程占有,當(dāng)前線程會被阻塞,當(dāng)前線程id則會被存儲到鎖對象對應(yīng)的Monitor對象的_EntryList屬性,并且將線程阻塞掛起。
  • 占有資源的線程繼續(xù)運(yùn)行,當(dāng)遇到monitorexit關(guān)鍵字的時候就要釋放鎖,此時jvm會喚醒隨機(jī)喚醒_EntryList里面的一個線程,被喚醒的線程會再次搶奪資源,沒有搶到資源的線程將會再次被阻塞,所以說synchronized是一個非公平的鎖。

那么鎖對象和Monitor對象是怎么關(guān)聯(lián)的呢?

是通過將Monitor對象的引用存儲到對象頭的Mark Word中實現(xiàn)的關(guān)聯(lián)。

Java1.6之前的synchronized大概就是這樣一個實現(xiàn)原理,之所以業(yè)界普遍認(rèn)為其性能很低是因為其有一個很大的弊端,就是每個線程在搶鎖的時候都要調(diào)用操作系統(tǒng)的底層api,這就導(dǎo)致用戶態(tài)到內(nèi)核態(tài)的切換,我們都知道Java程序性能最忌諱的就是用戶態(tài)與內(nèi)核態(tài)的來回切換。然而,我們程序并不每時每刻都會有很多線程競爭鎖資源,相反,大多數(shù)時間里,只有一個線程在執(zhí)行加鎖的邏輯,那么這種情況下每次都發(fā)生用戶態(tài)和內(nèi)核態(tài)切換無疑是沒有必要的性能消耗。所以業(yè)界對其性能持望而卻步的態(tài)度。

1.5后的鎖

JVM內(nèi)置鎖在1.5之后版本做了如下重大的優(yōu)化,在做了優(yōu)化后,其性能顯著提高,基本與ReentrantLock保持同等性能。

  • 鎖粗化
  • 鎖消除
  • 鎖升級:輕量級鎖 偏向鎖 適應(yīng)性自旋

接下來由淺入深講解優(yōu)化內(nèi)容

1. 鎖消除

鎖的消除,顧名思義就是將鎖去除,因為有些場景下鎖是可以去除的

public void sync()  {
        Sync sync=new Sync();
        synchronized(sync){

        }
}

如上這種情況,我們知道進(jìn)出一個方法就是當(dāng)前線程棧的入棧出棧,所以方法內(nèi)部只要不涉及共享資源操作就是線程安全的,如上這段代碼,sync對象聲明在方法內(nèi)部,其引用是局部變量,是線程獨(dú)享資源不是共享資源,是線程獨(dú)有資源,隨著出棧發(fā)生,對象也就銷毀了,因此此處是可以不用加鎖的,鎖消除優(yōu)化就是對這種情況進(jìn)行去除鎖的處理。

jvm如何進(jìn)行優(yōu)化的呢?jvm在JIT編譯時(可以簡單理解為當(dāng)某段代碼即將第一次被執(zhí)行時進(jìn)行編譯,又稱即時編譯),通過對運(yùn)行上下文的掃描,通過逃逸分析判斷方法中的是否存在共享資源,如果無共享資源則去除不存在共享資源競爭的鎖,從而節(jié)省請求鎖時間。

典型的案例是StringBuffer的使用,后續(xù)會講解。

通過上面我們知道鎖消除依賴逃逸分析,逃逸分析是可以通過jvm參數(shù)配置的,如下:

-XX:+DoEscapeAnalysis 開啟逃逸分析

-XX:+EliminateLocks 開啟鎖消除

逃逸分析可以簡單理解為分析資源是否能逃逸到其他方法或者其他線程中。

2. 鎖粗化

顧名思義,把小范圍的多個鎖變成大范圍少個鎖。

public Sync sync=new Sync();

public void sync()  {
        
        synchronized(sync){

        }
        synchronized(sync){

        }
        synchronized(sync){

        }
}

上面的代碼可以看出,一個邏輯被多次加同一把鎖,每一次上鎖都是會耗時的,所以完全可以把多個鎖合并為一個鎖,這樣只需要上一次鎖就可以了,大大節(jié)省了時間。

同樣jvm在即時編譯的時候會掃描判斷是否存在可以粗化的鎖行為。

3. 鎖膨脹

鎖膨脹又叫鎖升級。synchronized之所以能在性能上與ReentrantLock持平就得益于鎖膨脹的優(yōu)化。

鎖升級是鎖優(yōu)化后的鎖機(jī)制,這個機(jī)制中包含這樣幾個概念:偏向鎖,輕量級鎖,適應(yīng)性自旋,重量級鎖。在整個鎖膨脹的過程中對對象頭的依賴更加明顯。

鎖升級是依靠對象頭的Mark Word來保存標(biāo)志信息的,接下來以32位操作系統(tǒng)來看下鎖升級過程中的對象頭中運(yùn)行時數(shù)據(jù)的變化。

圖片圖片

  • 無鎖狀態(tài) 當(dāng)沒有任何線程進(jìn)入的時候,此時處于無鎖狀態(tài),Mark Word中會有25bit的空間大小留給hashcode,4bit的空間大小留給對象的分代年齡信息,1bit的空間大小是標(biāo)識是否偏向(0否,1是),2bit的空間大小是鎖標(biāo)識位。

此時鎖標(biāo)志位為01,是否偏向為0,代表無鎖狀態(tài)。但是此時并不一定有hashcode,因為hashcode是代碼運(yùn)行過程中調(diào)用生成方法才生成的,如果運(yùn)行過程不調(diào)用就不會生成。

請注意,hashcode的生成是會影響鎖的升級過程的。

  • 偏向鎖狀態(tài)

當(dāng)?shù)谝粋€線程T1進(jìn)入代碼塊后的步驟(前提條件是全程無hashcode生成)

  • 判斷是否處于偏向中(通過Mark Word中的是否偏向判斷)
  • 此時未處于偏向中,當(dāng)前線程會將自己的線程id保存到Mark Word中,設(shè)置是否偏向為1,此時鎖標(biāo)志位依然是01

此時Mark Word為:23bit的線程id,4bit的分代年齡,是否偏向為1,鎖標(biāo)識位依然為01。

此時是偏向鎖狀態(tài),它其實是一種特殊的無鎖狀態(tài)。

上面的過程是建立在全程無hashcode生成的基礎(chǔ)上,我們知道了hashcode會占用25bit,線程id會占用23bit,如果過程有hashcode生成怎么辦,這里涉及到兩個問題。

第一個問題,T1進(jìn)入前就已經(jīng)生成了hashcode怎么處理?

jvm的做法是如果偏向前已經(jīng)生成hashcode,那么就放棄偏向,直接進(jìn)入輕量級鎖。

第二個問題,T1進(jìn)入后鎖狀態(tài)變?yōu)榱似蜴i,此時生成hashcode怎么處理?

jvm的做法是撤銷偏向,直接進(jìn)入重量級鎖。

所以我們在使用鎖的時候要特別注意hashcode生成給鎖升級帶來的影響。

  • 輕量級鎖狀態(tài)

當(dāng)?shù)诙€線程T2進(jìn)入代碼塊后

  • 判斷是否處于偏向中(通過Mark Word中的是否偏向判斷)
  • 如果處于偏向中,T2會以cas的方式試圖將Mark Word中的線程id替換為自己的線程id
  • 如果T1已經(jīng)執(zhí)行完代碼塊,T2一定是可以替換成功的,此時鎖依然是偏向鎖狀態(tài)
  • 如果T1沒有執(zhí)行完代碼塊,T2一定是替換不成功的,此時將進(jìn)入偏向鎖撤銷升級為輕量級鎖的過程
  • 首先T1會進(jìn)入到安全點,T1和T2會在自己的??臻g開辟一塊區(qū)域用于保存鎖記錄,同時復(fù)制一份Mark Word到這個鎖記錄中,同時cas的方式將自己棧空間這個鎖記錄的指針設(shè)置到Mark Word中去,因為T1持有偏向鎖,所以T1會優(yōu)先設(shè)置成功,此時Mark Word中有30bit的鎖記錄指針和2bit的鎖標(biāo)志位,此時的鎖標(biāo)志位為00代表輕量級鎖,鎖記錄指針指向當(dāng)前持有輕量級鎖的線程中??臻g的地址。T2沒有替換成功,將會進(jìn)入不斷輪詢失敗重試過程。

輕量級鎖是在資源競爭壓力不是很大的情況下,避免每個線程都去獲取鎖而造成用戶態(tài)到內(nèi)核態(tài)的切換,這個切換是比較耗時的,這樣就能提高性能,但是如果競爭壓力大的情況下輕量級鎖就不行了,因為壓力大意味著有很多線程在輪序失敗重試獲取輕量級鎖,短時間內(nèi)會造成cpu壓力飆升,甚至拖垮cpu,這個時候就必須升級為重量級鎖。

那么如何才算競爭壓力大,什么時候會升級為重量級鎖呢?

jvm默認(rèn)輪詢次數(shù)限制值為十次,超過十次獲取不到資源就代表競爭壓力比較大了,用戶也可以使用如下參數(shù)配置來自行更改這個次數(shù)

-XX:PreBlockSpin

但是有個問題,如果通過這個默認(rèn)值或者這個jvm參數(shù)配置限制數(shù)量,那意味著jvm全系統(tǒng)的鎖都要遵循,這個數(shù)字可能不適用于所有的鎖,因此jvm引入了自適應(yīng)的自旋。自適應(yīng)意味著自旋的時間不再是固定的了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定的。如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運(yùn)行中,那么虛擬機(jī)就會認(rèn)為這次自旋也很有可能再次成功,進(jìn)而允許自旋等待持續(xù)相對更長的時間,比如持續(xù)100次忙循環(huán)。另一方面,如果對于某個鎖,自旋很少成功獲得過鎖,那在以后要獲取這個鎖時將有可能直接省略掉自旋過程,以避免浪費(fèi)處理器資源。有了自適應(yīng)自旋,隨著程序運(yùn)行時間的增長及性能監(jiān)控信息的不斷完善,虛擬機(jī)對程序鎖的狀況預(yù)測就會越來越精準(zhǔn),虛擬機(jī)就會變得越來越“聰明”了。

  • 重量級鎖

重量級鎖就是Monitor鎖,也叫監(jiān)視器鎖,其實現(xiàn)是依靠操作底層的互斥原語Mutes Lock,因為每一次獲取Monitor鎖都需要用戶態(tài)到內(nèi)核態(tài)的切換,所以比較耗時,也是重量級鎖的由來。 當(dāng)自旋的條件破壞后,比如自旋次數(shù)達(dá)到限制或者競爭的壓力越來越大,將不再自旋,輕量級鎖升級為重量級鎖,當(dāng)前對象頭中的Mark Word被復(fù)制一份到Monitor對象中,Mark Word中原來的輕量級鎖的鎖記錄指針被換成Monitor對象的指針,然后所有的線程會搶奪Monitor鎖的擁有權(quán),以cas方式將自己的線程id填充到Monitor對象的_owner字段,同時_count字段加1,當(dāng)然此時能夠cas成功的只會是原來持有輕量級鎖的線程,而那些沒有獲取到Monitor鎖的線程將會被阻塞并放入Monitor對象的_EntryList字段等待喚醒。

此時鎖的標(biāo)志位為10,表示重量級鎖。

當(dāng)線程退出Monitor鎖,便會將Monitor鎖中的_count減1,清空_owner,jvm會隨機(jī)喚醒_EntryList集合中一個線程重新獲取Monitor鎖,這個隨機(jī)便突出了synchronized的不公平性。

總結(jié)

在使用synchronized的時候一定要注意hashcode生成對鎖的影響,因為對象頭的mark word是保存對象運(yùn)行期數(shù)據(jù)的,這塊區(qū)域在32位機(jī)器上是32字節(jié),在64位機(jī)器上是64字節(jié),所以空間是有限的,是無法同時保存hashcode和輕量級鎖記錄指針或者M(jìn)onitor對象指針的,因為會做一些取舍。

synchronized鎖的隨機(jī)性決定了其非公平的特性。

synchronized鎖為什么是重量級鎖,為什么性能差,就是因為在優(yōu)化前,每個線程的進(jìn)入都會造成用戶態(tài)到內(nèi)核態(tài)的切換,而我們要的理想狀態(tài)是只有一個線程或者只有少量線程競爭的時候不進(jìn)行用戶態(tài)到內(nèi)核態(tài)的切換。從而提高性能。優(yōu)化后做到了。

責(zé)任編輯:武曉燕 來源: 碼農(nóng)本農(nóng)
相關(guān)推薦

2019-03-15 10:55:12

通信系統(tǒng)手機(jī)

2024-08-28 08:00:00

2025-03-20 12:33:36

2023-10-05 11:12:06

JUCUnsafe安全

2021-04-19 05:42:51

Mmap文件系統(tǒng)

2023-11-01 14:49:07

2022-12-06 17:30:04

2023-05-07 23:22:24

golang

2017-05-18 15:02:36

AndroidGC原理JVM內(nèi)存回收

2021-08-29 18:13:03

緩存失效數(shù)據(jù)

2014-10-08 15:00:50

SUSE操作系統(tǒng)云計算

2023-09-22 08:00:00

分布式鎖Redis

2020-09-24 06:44:54

HTTPS網(wǎng)站 HTTP

2022-10-21 08:17:13

MongoDB查詢Document

2020-11-27 10:34:01

HTTPHTTPS模型

2023-05-08 14:56:00

Kafka高可靠高性能

2021-02-11 08:59:37

SOLID模塊倒置原則

2018-06-26 15:00:24

Docker安全風(fēng)險

2022-06-01 07:49:43

索引數(shù)據(jù)Mysql

2020-10-22 08:01:52

XMLJSON轉(zhuǎn)換
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 国户精品久久久久久久久久久不卡 | 青青草精品视频 | 狠狠色综合久久丁香婷婷 | 欧美日韩精品久久久免费观看 | 国产高清免费视频 | 久久免费小视频 | 国产精品视频播放 | 91久久久久久 | 色久影院 | 亚洲精品视频一区二区三区 | 国产久 | 亚洲一区二区中文字幕 | av电影一区 | 国产乱码精品一区二区三区五月婷 | 美女毛片免费看 | 一区二区国产精品 | 91精品国产91 | 99色在线视频 | 日韩中文字幕视频在线观看 | 国产91精品网站 | 中文字幕欧美一区二区 | 久久久精品一区二区 | 欧美一级黄色片 | 欧美美女二区 | 久久高清亚洲 | 日韩成人在线播放 | 亚洲精彩免费视频 | 免费在线性爱视频 | 欧美日韩网站 | 在线成人免费视频 | 久草久| 久草视频观看 | 欧美久久影院 | 日韩中文一区二区 | 成人精品国产 | 亚洲精品小视频在线观看 | 色婷婷av一区二区三区软件 | 日本成人三级电影 | 久久精品国产一区二区三区不卡 | 亚洲人在线 | 成人精品一区二区三区中文字幕 |