<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    I want to fly higher
    programming Explorer
    posts - 114,comments - 263,trackbacks - 0
    1.Thread詳解,直接看示例代碼,注釋非常詳細

    package com.landon.mavs.example.concurrent;

    import java.lang.Thread.UncaughtExceptionHandler;
    import java.util.Arrays;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.concurrent.TimeUnit;

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    import com.landon.mavs.example.util.PrintUtil;

    /**
     * 
     * 線程基礎示例
     * 
     * <pre>
     * 1.public class Thread implements Runnable,其實現了Runnable接口.且如果其構造中傳入了Runnable
     * target,則其實現的run直接調用target.run.否則需要覆寫run 
     * 
     * 2.其內部定義了一個表示線程狀態的枚舉
     * {@link Thread.State}.包括NEW/RUNNABLE/BLOCKED/WAITING/TIMED_WAITING/TERMINATED
     * 6個狀態. 
     * 
     * 3.其內部還定義了一個接口{@link java.lang.Thread.UncaughtExceptionHandler}
     * ,用來表示線程因未捕獲的異常而突然終止時,調用處理程序的接口. 接口中只有一個方法:void uncaughtException(Thread t,
     * Throwable e).
     * {@link Thread#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)}
     * {@link Thread#getUncaughtExceptionHandler()}
     * {@link Thread#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)}},該方法是一個靜態方法.
     *     ->{@link ThreadGroup#uncaughtException(Thread, Throwable)}
     * 
     * 4.其內部定義了3個線程優先級常量,分別是{@link Thread#MIN_PRIORITY},{@link Thread#NORM_PRIORITY},{@link Thread#MAX_PRIORITY}
     *     ->三個常量值分別是1,5,10
     * </pre>
     * 
     * @author landon
     * 
     
    */

    public class ThreadBaseExample {
        
    private static final Logger LOGGER = LoggerFactory
                .getLogger(ThreadBaseExample.class);

        
    public static void main(String[] args) {
            
    // public Thread(ThreadGroup group, Runnable target, String name,long
            
    // stackSize)
            
    // 1.Thread構造方法最多可傳入4個參數,分別是線程組/run方法的目標任務/新線程的名稱/新線程的預期堆棧大小,為0表示忽略該參數
            
    // 2.Thread的其他7個構造方法也均是對這4個參數直接或間接的指定
            
    // 從源碼看:
            
    // 1.其內部實現均是調用了私有的private void init(ThreadGroup g, Runnable target,
            
    // String name,long stackSize)方法.
            
    // 2.構造如果未指定線程名字,則會初始化為"Thread-" +
            
    // nextThreadNum(),nextThreadNum則為線程的一個內部靜態計數器,從0開始(synchronized)
            
    // 3.如果未指定線程組group.則首先設置group為SecurityManager的線程組,如果還為空的話,則設置為當前調用線程所屬的線程組.
            
    // 同時isDaemon/priority屬性設置和當前調用線程一致.
            
    // 4.stackSize這個參數,從API
            
    // doc來看:該參數的作用具有高度的平臺依賴性(在某些平臺上,該參數可能不會起任何作用).因為使用它時要非常小心(所以API說要Java實現者文檔化該參數的實現行為).
            
    // (可參考JVM參數-Xss來理解stackSize).
            
    // 5.線程的tid也是根據nextThreadID生成,即也是一個內部計時器,從1開始(synchronized),區別于name的計數器從0開始(一個是++i,一個是i++)
            try {
                Thread t1 = new Thread(nullnullnull0);
                
    // 運行程序,發現報了一個空指針異常,因為第三個參數name傳了null.而在Thread的源碼中是用char[]存儲name的,即在init要對name進行進行name.toCharArray()操作.
                
    // 所以就報了空指針.所以如果用上面的構造函數,必須指定name.即name要么指定不為空,要么不指定自生成
                LOGGER.debug(
                        
    "t1.name:{},t1.groupName:{},t1.id:{},t1.isDaemon:{},t1.priority:{}",
                        t1.getName(), t1.getThreadGroup().getName(), t1.getId(),
                        t1.isDaemon(), t1.getPriority());
            }
     catch (Exception e) {
                LOGGER.warn("", e);
            }


            
    // 其內部調用:init(null, null, "Thread-" + nextThreadNum(), 0)
            Thread t2 = new Thread();
            
    // 輸出:t2.name:Thread-0,t2.groupName:main,t2.id:9,t2.isDaemon:false,t2.priority:5
            
    // 問題:為什么t2.id什么為9呢?因為線程id從1開始,也就是說已經初始化了8個線程
            
    // landon:1.因為jvm程序啟動后,會啟動一些守護線程(JVM內部線程).從下面的systemGroup輸出即可了解大概.
            
    // 2.用jstack查看了一下,另外啟動的三個線程是Low Memory Detector/C2 CompilerThread1/C2
            
    // CompilerThread0
            LOGGER.debug(
                    
    "t2.name:{},t2.groupName:{},t2.id:{},t2.isDaemon:{},t2.priority:{},t2.state:{}",
                    t2.getName(), t2.getThreadGroup().getName(), t2.getId(),
                    t2.isDaemon(), t2.getPriority(), t2.getState());
            
    // 獲得系統線程組
            ThreadGroup systemGroup = t2.getThreadGroup().getParent();
            Thread[] totalThreads = new Thread[systemGroup.activeCount()];
            systemGroup.enumerate(totalThreads);
            
    // 輸出: [Thread[Reference Handler,10,system], Thread[Finalizer,8,system],
            
    // Thread[Signal Dispatcher,9,system], Thread[Attach Listener,5,system],
            
    // Thread[main,5,main]
            
    // 從輸出可以看出,系統線程組下有多個活動線程,當然有一些是守護線程,除了main線程外,其余均是守護線程
            LOGGER.debug("application.totalThreads:{}",
                    Arrays.toString(totalThreads));
            
    // 從輸出可以看出,主線程main的id為1,即為初始化的第一個線程,然后是Reference
            
    // Handler(2)/Finalizer(3)/Signal Dispatcher(4)/Attach Listener(5)
            
    // 這5個均是jvm內部線程
            for (Thread t : totalThreads) {
                LOGGER.debug("{} .id:{}", t.getName(), t.getId());
            }

            ThreadGroup[] totalGroups = new ThreadGroup[systemGroup
                    .activeGroupCount()];
            systemGroup.enumerate(totalGroups);
            
    // 輸出:[java.lang.ThreadGroup[name=main,maxpri=10]] 也就是說系統線程組下只有一個主線程組
            LOGGER.debug("application.totalGroups:{}", Arrays.toString(totalGroups));

            Thread t3 = new Thread(new BaseExampleTask(), "t3");
            
    // Thread#public synchronized void start()
            
    // 使該線程開始執行(結果是兩個線程并發的執行,即當前調用start的線程以及另一個執行run的線程)
            
    // 不能多次啟動一個線程/當線程已經結束執行后,不能再重新啟動,否則會拋出java.lang.IllegalThreadStateException
            
    // 這個很重要.不要試圖有"重啟線程"這個想法.如果想復用線程,則任務可采用while(flag)形式,詳細可參考線程池實現
            
    // 可從os上來理解線程的本質(假如"線程可重啟的話",會有很多問題.1.拋出異常的任務你重啟有毛用 2.重啟后如何再提交任務?
            
    // 3.何時銷毀線程?內存泄露? 等等。。) // 此純屬landon個人猜測
            
    // 從源碼上看:
            
    // 1.判斷當前狀態,如果不是"NEW"或者this !=
            
    // me(線程初始化init的時候,會將this賦值給me),則拋出IllegalThreadStateException
            
    // 2.group.add(this)
            
    // 將自身加入到所屬的線程組中->即只有執行了start方法才會將線程加入到所屬的線程組中,這里是main線程組
            t3.start();
            
    try {
                LOGGER.debug("t3.state:{}", t3.getState());
                
    // 從輸出可以看到,拋出了IllegalThreadStateException.因為此時state已經是RUNNABLE了
                t3.start();
            }
     catch (Exception e) {
                LOGGER.warn("", e);
            }

            
    try {
                
    // 主線程等待t3執行完畢
                t3.join();
            }
     catch (Exception e) {
                LOGGER.warn("", e);
            }

            
    try {
                LOGGER.debug("t3.state:{}", t3.getState());
                
    // 從輸出可以看到,拋出了IllegalThreadStateException.因為此時state已經是TERMINATED了
                t3.start();
            }
     catch (Exception e) {
                LOGGER.warn("", e);
            }


            
    // Thread#public static int activeCount()
            
    // 該方法是一個靜態方法.返回當前調用線程所屬的線程組中活動線程的數目,而當前調用線程為main
            LOGGER.debug("main.group.activeCount:{}", Thread.activeCount());
            
    // Thread#public final void checkAccess()
            
    // 如果有SecurityManager,則調用security.checkAccess(this)
            
    // {@link SecurityManager#checkAccess(Thread t)}
            
    // 即判斷當前的調用線程是否有權限修改該線程,如果不允許則拋出SecurityException
            
    // 在setName/setPriority/setDaemon方法的實現中都均調用了checkAccess()方法,如果無權限修改則拋出SecurityException,則不會執行后續修改邏輯
            t3.checkAccess();
            
    // Thread#public final void setName(String name) 改變線程名稱
            t3.setName("t-modify-3");
            
    // Thread# 更改的線程優先級
            
    // 從源碼看:
            
    // 1.checkAccess 判斷是否有權限修改
            
    // 2.判斷參數的合法化,即參數必須要在[MIN_PRIORITY,MAX_PRIORITY],否則拋出IllegalArgumentException
            
    // 3.判斷所屬線程組的最大允許優先級,線程優先級被設置為指定的newPriority和所屬線程組的最大允許優先級中較小的一個{@link
            
    // ThreadGroup#setMaxPriority(int pri)}
            
    // ThreadGroup默認的maxPriority為parent的maxPriority,main的parent為system線程組,而system線程組的maxPriority為10
            t3.setPriority(Thread.MIN_PRIORITY);
            
    // 從這里輸出看發現t3的優先級并沒有被修改,還是5.why?看下面的解釋(因為線程已經運行結束.所屬線程組已經變為了null.所以設置優先級失敗)
            LOGGER.debug("t3.name:{},t3.priority:{}", t3.getName(),
                    t3.getPriority());
            
    // Thread#public final ThreadGroup getThreadGroup()
            
    // 返回該線所屬的線程組.注意,如果該線程已經停止運行,則該方法返回null.
            
    // 從輸出看,因為前面的代碼調用了join,即t3肯定已經結束了.所以輸出拋出了空指針異常
            
    // 從源碼看:
            
    // 1.Thread內部有一個私有的exit方法.private void exit().
            
    // 該方法應該是被系統調用,即線程真正退出的時候給線程一個清理的機會
            
    // 2.該方法的實現中,如果group不為null,則從group中移除自身并設置group為null.
            try {
                LOGGER.debug("mainGroup.maxPriority:{}", t3.getThreadGroup()
                        .getMaxPriority());
            }
     catch (Exception e) {
                LOGGER.warn("", e);
            }


            
    // Thread#public static native Thread currentThread() 靜態方法
            
    // 返回當前正在執行調用的線程對象的引用,即正在執行這句代碼的線程 而當前調用線程為主線程main
            LOGGER.debug("currentThread.name:{}", Thread.currentThread().getName());
            
    // Thread#public static void dumpStack() 靜態方法 將當前線程的堆棧跟蹤打印至標準錯誤流
            
    // 源碼實現:new Exception("Stack trace").printStackTrace();
            Thread.dumpStack();
            
    // 相當于Thread.dumpStack()
            new Exception("dump stack trace").printStackTrace();

            Thread[] mainActiveThreads = new Thread[Thread.activeCount()];
            
    // Thread#public static int enumerate(Thread tarray[]) 靜態方法
            
    // 將當前調用的線程組及其子組的另一個活動線程復制到指定的數組
            
    // 源碼實現:return currentThread().getThreadGroup().enumerate(tarray)
            
    // {@link ThreadGroup#enumerate(Thread list[])}
            Thread.enumerate(mainActiveThreads);
            LOGGER.debug("mainActiveThreads:{}", Arrays.toString(mainActiveThreads));

            
    // Thread#public static Map<Thread, StackTraceElement[]>
            
    // getAllStackTraces()
            
    // 靜態方法 返回所有活動線程的一個堆棧跟蹤的map.每個線程的堆棧跟蹤至代表一個快照
            
    // 源碼看:
            
    // 1.檢查SecurityManager,即是否有GET_STACK_TRACE_PERMISSION/MODIFY_THREADGROUP_PERMISSION的權限
            
    // 2.調用Thread內部的私有方法 private native static Thread[] getThreads()
            
    // 獲取線程列表快照
            
    // 3.調用Thread內部私有方法private native static StackTraceElement[][]
            
    // dumpThreads(Thread[] threads) 執行dump
            
    // 4.StackTraceElement[][]轉為map
            Map<Thread, StackTraceElement[]> mainActiveStackTraceMap = Thread
                    .getAllStackTraces();
            
    for (Entry<Thread, StackTraceElement[]> entry : mainActiveStackTraceMap
                    .entrySet()) {
                
    // 從輸出可以看出:如果當前沒有線程的堆棧跟蹤信息,則反返回一個零長度數組而非null.因為PrintUtil.printStackTrace未報空指針異常
                LOGGER.debug("mainActiveStackTraceMap.thread.name:{}", entry
                        .getKey().getName());
                PrintUtil.printStackTrace(entry.getValue(), System.err);
            }


            
    // Thread#public StackTraceElement[] getStackTrace()
            
    // 返回一個表示該線程堆棧轉儲的堆棧跟蹤元素數組.如果該線程尚未啟動或者已經終止,則該該方法返回一個零長度數組.{@link
            
    // Thread#EMPTY_STACK_TRACE},是一個private static final
            
    // StackTraceElement[].數組的第一個元素代表堆棧頂,其是該序列中最新的方法調用,最后一個元素代表堆棧底,是該序列中最舊的方法調用
            
    // 從源碼看:
            
    // 1.會檢查this和Thread.currentThread是否一致,如果不一致,則調用SecurityManager是否有GET_STACK_TRACE_PERMISSION
            
    // ->判斷線程是否alive,如果非alive,則返回EMPTY_STACK_TRACE
            
    // ->調用dumpThreads(new Thread[] {this})
            
    // 2.如果this == Thread.currentThread,則直接return (new
            
    // Exception()).getStackTrace()
            StackTraceElement[] mainThreadStackTraceElements = Thread
                    .currentThread().getStackTrace();
            PrintUtil.printStackTrace(mainThreadStackTraceElements, System.err);

            
    // Thread#public ClassLoader getContextClassLoader()
            
    // 返回該線程的上下文Classloader.上下文Classloader由線程創建者提供,供運行于該線程中的代碼在加載類和資源時使用.如果未設定,則默認為父線程的Classloader上下文.
            
    // 原始線程的上下文Classloader通常設定為用于加載應用程序的類加載器.
            
    // 從輸出的結果看:返回的是sun.misc.Launcher$AppClassLoader
            
    // 從源碼看:如果有SecurityManager且調用者的類加載器不為null且也不同于其上下文類加載器正在被請求的線程上下文類加載器的祖先,則check是否有GET_CLASSLOADER_PERMISSION的權限
            
    // {@link Thread#setContextClassLoader(ClassLoader cl)}
            ClassLoader mainContextClassLoader = Thread.currentThread()
                    .getContextClassLoader();
            LOGGER.debug("mainContextClassLoader:{}", mainContextClassLoader);

            
    // Thread#public static UncaughtExceptionHandler
            
    // getDefaultUncaughtExceptionHandler()
            
    // 靜態方法,返回線程由于未捕獲到異常而突然終止時調用的默認處理程序{@link
            
    // Thread#setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler
            
    // eh)}
            
    // 從源碼上看:
            
    // 1.當線程由于未捕獲到異常而突然終止時,是首先調用{link
            
    // Thread#getUncaughtExceptionHandler()},在私有的方法dispatchUncaughtException(Throwable
            
    // e)調用
            
    // 2.getUncaughtExceptionHandler首先判斷線程自身的uncaughtExceptionHandler是否為null,如果不為null,則直接返回當前;否則返回group.
            
    // =>因為ThreadGroup implements Thread.UncaughtExceptionHandler
            
    // 3.在ThreadGroup#uncaughtException方法中,判斷parent是否為null,如果不為null則調用parent.uncaughtException方法.
            
    // 否則則調用Thread.getDefaultUncaughtExceptionHandler()
            
    // 也就說線程的這個靜態DefaultUncaughtExceptionHandler是在線程自身未指定handler,線程的線程組系列(包括父線程組)也未指定uncaughtException方法,則才會轉至這個默認的handler
            UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread
                    .getDefaultUncaughtExceptionHandler();
            
    // 從輸出看,現在為null
            LOGGER.debug("defaultUncaughtExceptionHandler:{}",
                    defaultUncaughtExceptionHandler);

            
    // Thread#public static void
            
    // setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
            
    // 注意一個問題:不要設置handler為線程的ThreadGroup對象,否則會引起無限遞歸.
            
    // {@link ThreadGroup#uncaughtException(Thread t, Throwable e)}
            Thread.setDefaultUncaughtExceptionHandler(new ExampleDefaultUncaughtExceptionHandler());

            
    // 啟動了一個拋出異常的任務.從輸出看,執行了ExampleDefaultUncaughtExceptionHandler的uncaughtException方法
            Thread t4 = new Thread(new ExampleExceptionTask(), "t4");
            t4.start();

            
    // Thread#public UncaughtExceptionHandler getUncaughtExceptionHandler()
            
    // return uncaughtExceptionHandler != null ? uncaughtExceptionHandler :
            
    // group;
            
    // 輸出:t4.uncaughtExceptionHandler:java.lang.ThreadGroup[name=main,maxpri=10]
            
    // 從輸出看出,返回的是group
            LOGGER.debug("t4.uncaughtExceptionHandler:{}",
                    t4.getUncaughtExceptionHandler());

            Thread t5 = new Thread(new ExampleExceptionTask(), "t-5");
            
    // Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
            
    // 從輸出看,拋出異常時執行了{@link AUncaughtExcaptionHandler#uncaughtException}方法
            
    // 源碼實現中首先調用了 checkAccess(),即是否有權限修改
            t5.setUncaughtExceptionHandler(new AUncaughtExcaptionHandler());
            t5.start();

            
    // 從輸出看:com.landon.mavs.example.concurrent.ThreadBaseExample$AUncaughtExcaptionHandle
            
    // 即處處了線程設置的handler
            LOGGER.debug("t5.uncaughtExceptionHandler:{}",
                    t5.getUncaughtExceptionHandler());

            Thread t7 = new Thread(new ExampleDaemonTask(), "t7");
            t7.setDaemon(true);
            t7.start();
            
    try {
                
    // 這里主線程等待t7結束.因為主線程是用戶線程,所以t7可完全結束.
                t7.join();
            }
     catch (Exception e) {
                LOGGER.warn("t7.join.exception.", e);
            }


            Thread t6 = new Thread(new ExampleDaemonTask(), "t6");
            
    // Thread#public final void setDaemon(boolean on)
            
    // 將線程標記為守護線程或者用戶線程.當正在運行的線程都是守護線程時,Java虛擬機退出
            
    // 注意該方法必須在啟動線程前調用
            
    // 源碼上看:1.checkAccess
            
    // 2.判斷isAlive(),如果alive,則拋出IllegalThreadStateException 3.設置daemon屬性為參數
            t6.setDaemon(true);
            
    // 從輸出看,設置t6為守護線程后,任務只輸出了begin,未輸出end或者異常輸出.也就是說虛擬機直接退出了.未執行打斷守護線程類似的邏輯.
            t6.start();

            Thread t8 = new Thread("t8");
            
    // Thread#public String toString() 返回該線程的字符串表示形式
            
    // 返回:Thread[name,priority,group.name],如果group為null,則沒有group.name
            LOGGER.debug("t8:{}", t8.toString());

            
    // 靜態方法, 暫停當前正在執行的線程對象,并執行其他線程
            
    // yield()只是使當前線程重新回到可執行狀態,所以執行yield()的線程有可能在進入到可執行狀態后馬上又被執行
            
    // yield()只能使同優先級的線程有執行的機會
            Thread.yield();

            
    // 從輸出大致可以看到,每到2的倍數時就切換了一下線程
            for (int i = 0; i < 2; i++{
                
    new YieldThread("yt-" + i).start();
            }


            
    // Thread#public final native boolean isAlive() 測試線程是否處于活動狀態
            
    // 如果線程已經啟動且尚未終止則返回true
            LOGGER.debug("t6.isAlive:{}", t6.isAlive());// true 已啟動,但是為結束
            LOGGER.debug("t8.isAlive:{}", t8.isAlive());// false,因為還未啟動
            LOGGER.debug("t7.isAlive:{}", t7.isAlive());// false 已啟動,但是已結束

            
    // 測試Thread#holdsLock(Object obj)方法
            HoldsLockThread hlt = new HoldsLockThread();
            hlt.start();
        }


        
    private static class BaseExampleTask implements Runnable {

            @Override
            
    public void run() {
                LOGGER.debug("BaseExampleTask begin");

                
    // sleep模擬任務耗時
                try {
                    Thread.sleep(3 * 1000);
                }
     catch (Exception e) {
                    LOGGER.debug("BaseExampleTask was interrupted.");
                }


                LOGGER.debug("BaseExampleTask end");
            }


        }


        
    private static class ExampleExceptionTask implements Runnable {

            @Override
            
    public void run() {
                
    throw new RuntimeException();
            }


        }


        
    private static class ExampleDefaultUncaughtExceptionHandler implements
                UncaughtExceptionHandler {

            @Override
            
    public void uncaughtException(Thread t, Throwable e) {
                LOGGER.debug(String.format("thread.name:%s", t.getName()), e);
            }

        }


        
    private static class AUncaughtExcaptionHandler implements
                UncaughtExceptionHandler {

            @Override
            
    public void uncaughtException(Thread t, Throwable e) {
                LOGGER.debug("this a thread:{} UncaughtExcaptionHandler.", t);
                LOGGER.warn("", e);
            }

        }


        
    // 一個Daemon任務
        private static class ExampleDaemonTask implements Runnable {

            @Override
            
    public void run() {
                Thread curThread = Thread.currentThread();

                LOGGER.debug("ExampleDaemonTask" + "[" + curThread.getName() + "]"
                        
    + " begin");
                
    try {
                    
    // 這里讓sleep時間長了一下,看一下守護任務是否被終止
                    TimeUnit.SECONDS.sleep(10);
                }
     catch (InterruptedException e) {
                    LOGGER.warn("ExampleDaemonTask was interrupted.", e);
                }


                LOGGER.debug("ExampleDaemonTask" + "[" + curThread.getName() + "]"
                        
    + " end");
            }

        }


        
    private static class YieldThread extends Thread {
            
    public YieldThread(String name) {
                
    super(name);
            }


            @Override
            
    public void run() {
                
    for (int i = 3; i <= 10; i++{
                    LOGGER.debug(String.format("[%s]:%d", getName(), i));

                    
    if (i % 2 == 0{
                        
    // 執行yield,暫停一下,讓出cpu
                        Thread.yield();
                    }

                }

            }

        }


        
    private static class HoldsLockThread extends Thread {
            
    private final Object lock = new Object();

            @Override
            
    public void run() {
                
    try {
                    LOGGER.debug(
    "HoldsLockThread.holdsLock:{}", holdsLock(lock));// false
                    synchronized (lock) {
                        lock.wait(
    1 * 1000);
                        
    // Thread#public static native boolean holdsLock(Object obj)
                        
    // 靜態方法 當且僅當當前線程在指定對象上保持監視器鎖時,才返回true
                        LOGGER.debug("HoldsLockThread.holdsLock:{}",
                                holdsLock(lock));
    // true
                    }

                    LOGGER.debug(
    "HoldsLockThread.holdsLock:{}", holdsLock(lock));// false
                }
     catch (InterruptedException e) {
                }

            }

        }

    }



    2.本篇結合jdk源碼+示例代碼詳細介紹了Thread的API.
    posted on 2013-12-23 15:23 landon 閱讀(1706) 評論(0)  編輯  收藏 所屬分類: Program
    主站蜘蛛池模板: 一二三四在线观看免费高清中文在线观看 | 国产成人精品亚洲日本在线| 好看的电影网站亚洲一区| 黑人大战亚洲人精品一区| 久久精品国产亚洲Aⅴ蜜臀色欲| 免费h黄肉动漫在线观看| 国产免费AV片无码永久免费| 免费大香伊蕉在人线国产| 免费一级毛片清高播放| av无码东京热亚洲男人的天堂| 亚洲精品无码久久久久AV麻豆| 亚洲精品无码99在线观看 | a级毛片无码免费真人| 噼里啪啦电影在线观看免费高清| 性色av无码免费一区二区三区| 成人一a毛片免费视频| 免费毛片在线播放| 亚洲精品线路一在线观看 | 久久久久国色AV免费观看性色| 四虎影视www四虎免费| 国产伦一区二区三区免费 | 大学生高清一级毛片免费| 日本免费中文字幕在线看| 亚洲国模精品一区| 亚洲精品国产精品乱码视色| 亚洲精品资源在线| 亚洲一区欧洲一区| 美女被暴羞羞免费视频| 青青操视频在线免费观看| 2015日韩永久免费视频播放 | 两个人看的www高清免费观看| 日韩精品无码一区二区三区免费| 在线视频免费观看爽爽爽| 国产美女精品久久久久久久免费| 亚洲人成网站在线观看青青| 亚洲男人第一av网站| 亚洲成a人片在线观看精品| 瑟瑟网站免费网站入口| 日本一区二区免费看| 成人毛片免费视频| 国产亚洲av片在线观看18女人|