久久精品国产精品国产精品污,男人扒开添女人下部免费视频,一级国产69式性姿势免费视频,夜鲁夜鲁很鲁在线视频 视频,欧美丰满少妇一区二区三区,国产偷国产偷亚洲高清人乐享,中文 在线 日韩 亚洲 欧美,熟妇人妻无乱码中文字幕真矢织江,一区二区三区人妻制服国产

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android复习系列②之《Java进阶》

發(fā)布時(shí)間:2024/3/26 Android 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android复习系列②之《Java进阶》 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1 java多線程(非常重要)

1.1. 線程

線程和進(jìn)程的區(qū)別?

線程是CPU調(diào)度的最小單位,一個(gè)進(jìn)程中可以包含多個(gè)線程,在Android中,一個(gè)進(jìn)程通常是一個(gè)App,App中會(huì)有一個(gè)主線程,主線程可以用來操作界面元素,如果有耗時(shí)的操作,必須開啟子線程執(zhí)行,不然會(huì)出現(xiàn)ANR,除此以外,進(jìn)程間的數(shù)據(jù)是獨(dú)立的,線程間的數(shù)據(jù)可以共享。

java多線程實(shí)現(xiàn)方式主要有:

  • 繼承Thread
    優(yōu)點(diǎn) : 方便傳參,可以在子類添加成員變量,通過方法設(shè)置參數(shù)或構(gòu)造函數(shù)傳參。
    缺點(diǎn)
    1.因?yàn)镴ava不支持多繼承,所以繼承了Thread類以后,就無法繼承其他類。
    2.每次都要新建一個(gè)類,不支持通過線程池操作,創(chuàng)建和銷毀線程對(duì)資源的開銷比較大。
    3.從代碼結(jié)構(gòu)上講,為了啟動(dòng)一個(gè)線程任務(wù),都要?jiǎng)?chuàng)建一個(gè)類,耦合性太高。
    4.無法獲取線程任務(wù)的返回結(jié)果。

    Thread syncTask = new Thread() {@Overridepublic void run() {// 執(zhí)行耗時(shí)操作}};syncTask.start();//啟動(dòng)線程
  • 實(shí)現(xiàn)Runnable
    優(yōu)點(diǎn) : 此方式可以繼承其他類。也可以使用線程池管理,節(jié)約資源。創(chuàng)建線程代碼的耦合性較低。推薦使用此種方式創(chuàng)建線程。
    缺點(diǎn): 不方便傳參,只能使用主線程中用final修飾的變量。其次是無法獲取線程任務(wù)的返回結(jié)果。

    //寫法1:集成Runnable接口定義任務(wù)類 public class ThreadTask implements Runnable {@Overridepublic void run() {while(true) {System.out.println(Thread.currentThread().getName()+" is running...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}} } //在其他地方使用 new Thread(new ThreadTask ()).start(); //寫法2:匿名內(nèi)部類寫法 new Thread(new Runnable() {@Overridepublic void run() {//做操作 } }).start();
  • 實(shí)現(xiàn)Callable
    此種方式創(chuàng)建線程底層源碼也是使用實(shí)現(xiàn)Runnable接口的方式實(shí)現(xiàn)的,所以不是一種新的創(chuàng)建線程的方式,只是在實(shí)現(xiàn)Runnable接口方式創(chuàng)建線程的基礎(chǔ)上,同時(shí)實(shí)現(xiàn)了Future接口,實(shí)現(xiàn)有返回值的創(chuàng)建線程。

    Runnable 與 Callable的區(qū)別:

    1. Runnable是在JDK1.0的時(shí)候提出的多線程的實(shí)現(xiàn)接口,而Callable是在JDK1.5之后提出的; 2. Runnable 接口之中只提供了一個(gè)run()方法,并且沒有返回值; 3. Callable接口提供有call(),可以有返回值;

    擴(kuò)展:

    Callable接口支持返回執(zhí)行結(jié)果,此時(shí)需要調(diào)用FutureTask.get()方法實(shí)現(xiàn),此方法會(huì)阻塞主線程直到獲取‘將來’結(jié)果; 當(dāng)不調(diào)用此方法時(shí),主線程不會(huì)阻塞public class CallableImpl implements Callable<String> {public CallableImpl(String acceptStr) {this.acceptStr = acceptStr;}private String acceptStr;@Overridepublic String call() throws Exception {// 任務(wù)阻塞 1 秒Thread.sleep(1000);return this.acceptStr + " append some chars and return it!";}public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<String> callable = new CallableImpl("my callable test!");FutureTask<String> task = new FutureTask<>(callable);long beginTime = System.currentTimeMillis();// 創(chuàng)建線程new Thread(task).start();// 調(diào)用get()阻塞主線程,反之,線程不會(huì)阻塞String result = task.get();long endTime = System.currentTimeMillis();System.out.println("hello : " + result);System.out.println("cast : " + (endTime - beginTime) / 1000 + " second!");} }//執(zhí)行結(jié)果hello : my callable test! append some chars and return it! cast : 1 second!

    總結(jié)

    根據(jù)Oracle提供的JAVA官方文檔的說明,Java創(chuàng)建線程的方法只有兩種方式,即繼承Thread類和實(shí)現(xiàn)Runnable接口。其他所有創(chuàng)建線程的方式,底層都是使用這兩種方式中的一種實(shí)現(xiàn)的,比如通過線程池、通過匿名類、通過lambda表達(dá)式、通過Callable接口等等,全是通過這兩種方式中的一種實(shí)現(xiàn)的。所以我們?cè)谡莆站€程創(chuàng)建的時(shí)候,必須要掌握的只有這兩種,通過文章中優(yōu)缺點(diǎn)的分析,這兩種方法中,最為推薦的就是實(shí)現(xiàn)Runnable接口的方式去創(chuàng)建線程。

  • 1.2. 線程的狀態(tài)有哪些?

    Java中定義線程的狀態(tài)有6種,可以查看Thread類的State枚舉:

    public static enum State{NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;private State() {}}
    • 初始(NEW):新創(chuàng)建了一個(gè)線程對(duì)象,還沒調(diào)用start方法;
    • 運(yùn)行(RUNNABLE):java線程中將就緒(ready)和運(yùn)行中(running)統(tǒng)稱為運(yùn)行(RUNNABLE)。線程創(chuàng)建后調(diào)用了該對(duì)象的start方法,此時(shí)處于就緒狀態(tài),當(dāng)獲得CPU時(shí)間片后變?yōu)檫\(yùn)行中狀態(tài);
    • 阻塞(BLOCKED):表現(xiàn)線程阻塞于鎖;
    • 等待(WAITING):進(jìn)入該狀態(tài)的線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷);
    • 超時(shí)等待(TIMED_WAITING):該狀態(tài)不同于WAITING,它可以在指定時(shí)間后自行返回;
    • 終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢。

    狀態(tài)詳細(xì)說明

  • 初始狀態(tài)(NEW)
    實(shí)現(xiàn)Runnable接口和繼承Thread可以得到一個(gè)線程類,new一個(gè)實(shí)例出來,線程就進(jìn)入了初始狀態(tài)。

  • 就緒狀態(tài)(RUNNABLE之READY)
    就緒狀態(tài)只是說你資格運(yùn)行,調(diào)度程序沒有挑選到你,你就永遠(yuǎn)是就緒狀態(tài)。
    調(diào)用線程的start()方法,此線程進(jìn)入就緒狀態(tài)。
    當(dāng)前線程sleep()方法結(jié)束,其他線程join()結(jié)束,等待用戶輸入完畢,某個(gè)線程拿到對(duì)象鎖,這些線程也將進(jìn)入就緒狀態(tài)。
    當(dāng)前線程時(shí)間片用完了,調(diào)用當(dāng)前線程的yield()方法,當(dāng)前線程進(jìn)入就緒狀態(tài)。
    鎖池里的線程拿到對(duì)象鎖后,進(jìn)入就緒狀態(tài)。

    運(yùn)行中狀態(tài)(RUNNABLE之RUNNING)
    線程調(diào)度程序從可運(yùn)行池中選擇一個(gè)線程作為當(dāng)前線程時(shí)線程所處的狀態(tài)。這也是線程進(jìn)入運(yùn)行狀態(tài)的唯一的一種方式。

  • 阻塞狀態(tài)(BLOCKED)
    阻塞狀態(tài)是線程阻塞在進(jìn)入synchronized關(guān)鍵字修飾的方法或代碼塊(獲取鎖)時(shí)的狀態(tài)。

  • 等待(WAITING)
    處于這種狀態(tài)的線程不會(huì)被分配CPU執(zhí)行時(shí)間,它們要等待被顯式地喚醒,否則會(huì)處于無限期等待的狀態(tài)。

  • 超時(shí)等待(TIMED_WAITING)
    處于這種狀態(tài)的線程不會(huì)被分配CPU執(zhí)行時(shí)間,不過無須無限期等待被其他線程顯示地喚醒,在達(dá)到一定時(shí)間后它們會(huì)自動(dòng)喚醒。

  • 終止?fàn)顟B(tài)(TERMINATED)
    當(dāng)線程的run()方法完成時(shí),或者主線程的main()方法完成時(shí),我們就認(rèn)為它終止了。這個(gè)線程對(duì)象也許是活的,但是它已經(jīng)不是一個(gè)單獨(dú)執(zhí)行的線程。線程一旦終止了,就不能復(fù)生。
    在一個(gè)終止的線程上調(diào)用start()方法,會(huì)拋出java.lang.IllegalThreadStateException異常。

  • 1.3. 線程的狀態(tài)轉(zhuǎn)換及控制

    主要由這幾個(gè)方法來控制:sleep、join、yield、wait、notify以及notifyAll。

    wait() / notify() / notifyAll()

    wait(),notify(),notifyAll() 是定義在Object類的實(shí)例方法,用于控制線程狀態(tài),三個(gè)方法都必須在synchronized 同步關(guān)鍵字所限定的作用域中調(diào)用(只能在同步控制方法或者同步控制塊中使用),否則會(huì)報(bào)錯(cuò) java.lang.IllegalMonitorStateException。

    join() / sleep() / yield()

    join()

    如果線程A調(diào)用了線程B的join方法,線程A將被阻塞,等待線程B執(zhí)行完畢后線程A才會(huì)被執(zhí)行。這里需要注意一點(diǎn)的是,join方法必須在線程B的start方法調(diào)用之后調(diào)用才有意義。join方法的主要作用就是實(shí)現(xiàn)線程間的同步,它可以使線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行。

    sleep()

    當(dāng)線程A調(diào)用了 sleep方法,則線程A將被阻塞,直到指定睡眠的時(shí)間到達(dá)后,線程A才會(huì)重新被喚起,進(jìn)入就緒狀態(tài)。

    public class Test {public static void main(String[] args) {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);try {Thread.sleep(1000); // 阻塞當(dāng)前線程1s} catch (Exception e) {e.printStackTrace();}}} }

    yield()  當(dāng)線程A調(diào)用了yield方法,它可以暫時(shí)放棄處理器,但是線程A不會(huì)被阻塞,而是進(jìn)入就緒狀態(tài)。執(zhí)行了yield方法的線程什么時(shí)候會(huì)繼續(xù)運(yùn)行由線程調(diào)度器來決定。

    public class YieldThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);// 主動(dòng)放棄Thread.yield();}} }

    sleep方法和wait方法的區(qū)別是什么?

    wait方法既釋放cpu,又釋放鎖。 sleep方法只釋放cpu,但是不釋放鎖。

    sleep 方法是Thread類的一個(gè)靜態(tài)方法,其作用是使運(yùn)行中的線程暫時(shí)停止指定的毫秒數(shù),從而該線程進(jìn)入阻塞狀態(tài)并讓出處理器,將執(zhí)行的機(jī)會(huì)讓給其他線程。但是這個(gè)過程中監(jiān)控狀態(tài)始終保持,當(dāng)sleep的時(shí)間到了之后線程會(huì)自動(dòng)恢復(fù)。

    wait 方法是Object類的方法,它是用來實(shí)現(xiàn)線程同步的。當(dāng)調(diào)用某個(gè)對(duì)象的wait方法后,當(dāng)前線程會(huì)被阻塞并釋放同步鎖,直到其他線程調(diào)用了該對(duì)象的 notify 方法或者 notifyAll 方法來喚醒該線程。所以 wait 方法和 notify(或notifyAll)應(yīng)當(dāng)成對(duì)出現(xiàn)以保證線程間的協(xié)調(diào)運(yùn)行。

    1.4. Java如何正確停止線程

    注意:Java中線程的stop()、suspend()、resume()三個(gè)方法都已經(jīng)被棄用,所以不再使用stop()方法停止線程。

    我們只能調(diào)用線程的interrupt()方法通知系統(tǒng)停止線程,并不能強(qiáng)制停止線程。線程能否停止,何時(shí)停止,取決于系統(tǒng)。

    1.5 線程池(非常重要)

    線程池的地位十分重要,基本上涉及到跨線程的框架都使用到了線程池,比如說OkHttp、RxJava、LiveData以及協(xié)程等。

    與新建一個(gè)線程相比,線程池的特點(diǎn)?

  • 節(jié)省開銷: 線程池中的線程可以重復(fù)利用。
  • 速度快:任務(wù)來了就能開始,省去創(chuàng)建線程的時(shí)間。
  • 線程可控:線程數(shù)量可空和任務(wù)可控。
  • 功能強(qiáng)大:可以定時(shí)和重復(fù)執(zhí)行任務(wù)。
  • ExecutorService簡(jiǎn)介

    通常來說我們說到線程池第一時(shí)間想到的就是它:ExecutorService,它是一個(gè)接口,其實(shí)如果要從真正意義上來說,它可以叫做線程池的服務(wù),因?yàn)樗峁┝吮姸嘟涌赼pi來控制線程池中的線程,而真正意義上的線程池就是:ThreadPoolExecutor,它實(shí)現(xiàn)了ExecutorService接口,并封裝了一系列的api使得它具有線程池的特性,其中包括工作隊(duì)列、核心線程數(shù)、最大線程數(shù)等。

    線程池(ThreadPoolExecutor)中的幾個(gè)參數(shù)是什么意思?

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) {//...}

    參數(shù)解釋如下(重要):

    corePoolSize:核心線程數(shù)量,不會(huì)釋放。

    maximumPoolSize:允許使用的最大線程池?cái)?shù)量,非核心線程數(shù)量,閑置時(shí)會(huì)釋放。

    keepAliveTime:閑置線程允許的最大閑置時(shí)間。它起作用必須在一個(gè)前提下,就是當(dāng)線程池中的線程數(shù)量超過了corePoolSize時(shí),它表示多余的空閑線程的存活時(shí)間,即:多余的空閑線程在超過keepAliveTime時(shí)間內(nèi)沒有任務(wù)的話則被銷毀。而這個(gè)主要應(yīng)用在緩存線程池中

    unit:閑置時(shí)間的單位。

    workQueue:阻塞隊(duì)列,用來存儲(chǔ)已經(jīng)提交但未被執(zhí)行的任務(wù),不同的阻塞隊(duì)列有不同的特性。

    threadFactory:線程工廠,用來創(chuàng)建線程池中的線程,通常用默認(rèn)的即可

    handler:通常叫做拒絕策略,1、在線程池已經(jīng)關(guān)閉的情況下 2、任務(wù)太多導(dǎo)致最大線程數(shù)和任務(wù)隊(duì)列已經(jīng)飽和,無法再接收新的任務(wù) 。在上面兩種情況下,只要滿足其中一種時(shí),在使用execute()來提交新的任務(wù)時(shí)將會(huì)拒絕,而默認(rèn)的拒絕策略是拋一個(gè)RejectedExecutionException異常

    上面的參數(shù)理解起來都比較簡(jiǎn)單,不過workQueue這個(gè)任務(wù)隊(duì)列卻要再次說明一下,它是一個(gè)BlockingQueue<Runnable>對(duì)象,而泛型則限定它是用來存放Runnable對(duì)象的,剛剛上面講了,不同的線程池它的任務(wù)隊(duì)列實(shí)現(xiàn)肯定是不一樣的,所以,保證不同線程池有著不同的功能的核心就是這個(gè)workQueue的實(shí)現(xiàn)了,細(xì)心的會(huì)發(fā)現(xiàn)在剛剛的用來創(chuàng)建線程池的工廠方法中,針對(duì)不同的線程池傳入的workQueue也不一樣,五種線程池分別用的是什么BlockingQueue:

    1、newFixedThreadPool()—>LinkedBlockingQueue 無界的隊(duì)列 2、newSingleThreadExecutor()—>LinkedBlockingQueue 無界的隊(duì)列 3、newCachedThreadPool()—>SynchronousQueue 直接提交的隊(duì)列 4、newScheduledThreadPool()—>DelayedWorkQueue 等待隊(duì)列 5、newSingleThreadScheduledExecutor()—>DelayedWorkQueue 等待隊(duì)列

    線程池中用到的三種阻塞隊(duì)列:

  • LinkedBlockingQueue:無界的隊(duì)列

    它的容量是 Integer.MAX_VALUE,為 231 -1 ,是一個(gè)非常大的值,可以認(rèn)為是無界隊(duì)列。FixedThreadPool 和 SingleThreadExecutor 線程池的線程數(shù)是固定的,所以沒有辦法增加特別多的線程來處理任務(wù),這時(shí)就需要 LinkedBlockingQueue 這樣一個(gè)沒有容量限制的阻塞隊(duì)列來存放任務(wù)

  • SynchronousQueue:直接提交的隊(duì)列

    如果不希望任務(wù)在隊(duì)列中等待而是希望將任務(wù)直接移交給工作線程,可使用SynchronousQueue作為等待隊(duì)列。SynchronousQueue不是一個(gè)真正的隊(duì)列,而是一種線程之間移交的機(jī)制。要將一個(gè)元素放入SynchronousQueue中,必須有另一個(gè)線程正在等待接收這個(gè)元素。只有在使用無界線程池或者有飽和策略時(shí)才建議使用該隊(duì)列。

  • DelayedWorkQueue:等待隊(duì)列

    它對(duì)應(yīng)的線程池分別是 ScheduledThreadPool 和 SingleThreadScheduledExecutor,這兩種線程池的最大特點(diǎn)就是可以延遲執(zhí)行任務(wù),比如說一定時(shí)間后執(zhí)行任務(wù)或是每隔一定的時(shí)間執(zhí)行一次任務(wù)。

    DelayedWorkQueue 的特點(diǎn)是內(nèi)部元素并不是按照放入的時(shí)間排序,而是會(huì)按照延遲的時(shí)間長(zhǎng)短對(duì)任務(wù)進(jìn)行排序,內(nèi)部采用的是“堆”的數(shù)據(jù)結(jié)構(gòu)(堆的應(yīng)用之一就是 優(yōu)先級(jí)隊(duì)列)。之所以線程池 ScheduledThreadPool 和 SingleThreadScheduledExecutor 選擇 DelayedWorkQueue,是因?yàn)樗鼈儽旧碚腔跁r(shí)間執(zhí)行任務(wù)的,而延遲隊(duì)列正好可以把任務(wù)按時(shí)間進(jìn)行排序,方便任務(wù)的執(zhí)行。

  • 線程池的種類有哪些:五種功能不一樣的線程池

    這樣創(chuàng)建線程池的話,我們需要配置一堆東西,非常麻煩。所以,官方也不推薦使用這種方法來創(chuàng)建線程池,而是推薦使用Executors的工廠方法來創(chuàng)建線程池,Executors類是官方提供的一個(gè)工廠類,它里面封裝好了眾多功能不一樣的線程池(但底層實(shí)現(xiàn)還是通過ThreadPoolExecutor),從而使得我們創(chuàng)建線程池非常的簡(jiǎn)便,主要提供了如下五種功能不一樣的線程池:

    newCachedThreadPool() :返回一個(gè)可以根據(jù)實(shí)際情況調(diào)整線程池中線程的數(shù)量的線程池。即該線程池中的線程數(shù)量不確定,是根據(jù)實(shí)際情況動(dòng)態(tài)調(diào)整的。

    newFixedThreadPool() :線程池只能存放指定數(shù)量的線程池,線程不會(huì)釋放,可重復(fù)利用。

    newSingleThreadExecutor() :單線程的線程池。即每次只能執(zhí)行一個(gè)線程任務(wù),多余的任務(wù)會(huì)保存到一個(gè)任務(wù)隊(duì)列中,等待這一個(gè)線程空閑,當(dāng)這個(gè)線程空閑了再按FIFO方式順序執(zhí)行任務(wù)隊(duì)列中的任務(wù)。

    newScheduledThreadPool() :可定時(shí)和重復(fù)執(zhí)行的線程池。

    newSingleThreadScheduledExecutor():同上。和上面的區(qū)別是該線程池大小為1,而上面的可以指定線程池的大小。

    通過Executors的工廠方法來獲取

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(); ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();

    通過Executors的工廠方法來創(chuàng)建線程池極其簡(jiǎn)便,其實(shí)它的內(nèi)部還是通過new ThreadPoolExecutor(…)的方式創(chuàng)建線程池的,我們看一下這些工廠方法的內(nèi)部實(shí)現(xiàn):

    public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());}public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

    線程池ThreadPoolExecutor的使用

    使用線程池,其中涉及到一個(gè)極其重要的方法,即:

    execute(Runnable command)

    該方法意為執(zhí)行給定的任務(wù),該任務(wù)處理可能在新的線程、已入池的線程或者正調(diào)用的線程,這由ThreadPoolExecutor的實(shí)現(xiàn)決定。

    五種線程池使用舉例

  • newFixedThreadPool 創(chuàng)建一個(gè)固定線程數(shù)量的線程池,示例為:
    創(chuàng)建了一個(gè)線程數(shù)為3的固定線程數(shù)量的線程池,同理該線程池支持的線程最大并發(fā)數(shù)也是3,而我模擬了10個(gè)任務(wù)讓它處理,執(zhí)行的情況則是首先執(zhí)行前三個(gè)任務(wù),后面7個(gè)則依次進(jìn)入任務(wù)隊(duì)列進(jìn)行等待,執(zhí)行完前三個(gè)任務(wù)后,再通過FIFO的方式從任務(wù)隊(duì)列中取任務(wù)執(zhí)行,直到最后任務(wù)都執(zhí)行完畢。

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 1; i <= 10; i++) {final int index = i;fixedThreadPool.execute(new Runnable() {@Overridepublic void run() {String threadName = Thread.currentThread().getName();Log.v("zxy", "線程:"+threadName+",正在執(zhí)行第" + index + "個(gè)任務(wù)");try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}

  • newSingleThreadExecutor
    創(chuàng)建一個(gè)只有一個(gè)線程的線程池,每次只能執(zhí)行一個(gè)線程任務(wù),多余的任務(wù)會(huì)保存到一個(gè)任務(wù)隊(duì)列中,等待線程處理完再依次處理任務(wù)隊(duì)列中的任務(wù),示例為:

    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();for (int i = 1; i <= 10; i++) {final int index = i;singleThreadPool.execute(new Runnable() {@Overridepublic void run() {String threadName = Thread.currentThread().getName();Log.v("zxy", "線程:"+threadName+",正在執(zhí)行第" + index + "個(gè)任務(wù)");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});}


    其實(shí)我們通過newSingleThreadExecutor()和newFixedThreadPool()的方法發(fā)現(xiàn),創(chuàng)建一個(gè)singleThreadExecutorPool實(shí)際上就是創(chuàng)建一個(gè)核心線程數(shù)和最大線程數(shù)都為1的fixedThreadPool。

  • newCachedThreadPool
    創(chuàng)建一個(gè)可以根據(jù)實(shí)際情況調(diào)整線程池中線程的數(shù)量的線程池,為了體現(xiàn)該線程池可以自動(dòng)根據(jù)實(shí)現(xiàn)情況進(jìn)行線程的重用,而不是一味的創(chuàng)建新的線程去處理任務(wù),我設(shè)置了每隔1s去提交一個(gè)新任務(wù),這個(gè)新任務(wù)執(zhí)行的時(shí)間也是動(dòng)態(tài)變化的,示例為

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 1; i <= 10; i++) {final int index = i;try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {@Overridepublic void run() {String threadName = Thread.currentThread().getName();Log.v("zxy", "線程:" + threadName + ",正在執(zhí)行第" + index + "個(gè)任務(wù)");try {long time = index * 500;Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}});}

  • newScheduledThreadPool
    創(chuàng)建一個(gè)可以定時(shí)或者周期性執(zhí)行任務(wù)的線程池,示例為:

    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);//延遲2秒后執(zhí)行該任務(wù)scheduledThreadPool.schedule(new Runnable() {@Overridepublic void run() {}}, 2, TimeUnit.SECONDS);//延遲1秒后,每隔2秒執(zhí)行一次該任務(wù)scheduledThreadPool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {}}, 1, 2, TimeUnit.SECONDS);
  • newSingleThreadScheduledExecutor
    創(chuàng)建一個(gè)可以定時(shí)或者周期性執(zhí)行任務(wù)的線程池,該線程池的線程數(shù)為1,示例為

    ScheduledExecutorService singleThreadScheduledPool = Executors.newSingleThreadScheduledExecutor();//延遲1秒后,每隔2秒執(zhí)行一次該任務(wù)singleThreadScheduledPool.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {String threadName = Thread.currentThread().getName();Log.v("zxy", "線程:" + threadName + ",正在執(zhí)行");}},1,2,TimeUnit.SECONDS);

    這個(gè)和上面的沒什么太大區(qū)別,只不過是線程池內(nèi)線程數(shù)量的不同,效果為:每隔2秒就會(huì)執(zhí)行一次該任務(wù)

  • 自定義線程池ThreadPoolExecutor(自行了解)

    線程池的狀態(tài):

  • RUNNING:線程池一旦被創(chuàng)建,就處于 RUNNING 狀態(tài),任務(wù)數(shù)為 0,能夠接收新任務(wù),對(duì)已排隊(duì)的任務(wù)進(jìn)行處理。

  • SHUTDOWN:不接收新任務(wù),但能處理已排隊(duì)的任務(wù)。調(diào)用線程池的 shutdown() 方法,線程池由 RUNNING 轉(zhuǎn)變?yōu)?SHUTDOWN 狀態(tài)。

  • STOP:不接收新任務(wù),不處理已排隊(duì)的任務(wù),并且會(huì)中斷正在處理的任務(wù)。調(diào)用線程池的 shutdownNow() 方法,線程池由(RUNNING 或 SHUTDOWN ) 轉(zhuǎn)變?yōu)?STOP 狀態(tài)。

  • TIDYING:SHUTDOWN 狀態(tài)下,任務(wù)數(shù)為 0, 其他所有任務(wù)已終止,線程池會(huì)變?yōu)?TIDYING 狀態(tài),會(huì)執(zhí)行 terminated() 方法。線程池中的 terminated() 方法是空實(shí)現(xiàn),可以重寫該方法進(jìn)行相應(yīng)的處理。

    線程池在 SHUTDOWN 狀態(tài),任務(wù)隊(duì)列為空且執(zhí)行中任務(wù)為空,線程池就會(huì)由 SHUTDOWN 轉(zhuǎn)變?yōu)?TIDYING 狀態(tài)。

    線程池在 STOP 狀態(tài),線程池中執(zhí)行中任務(wù)為空時(shí),就會(huì)由 STOP 轉(zhuǎn)變?yōu)?TIDYING 狀態(tài)。

  • TERMINATED:線程池徹底終止。線程池在 TIDYING 狀態(tài)執(zhí)行完 terminated() 方法就會(huì)由 TIDYING 轉(zhuǎn)變?yōu)?TERMINATED 狀態(tài)。

  • 線程池的停止
    關(guān)于線程池的停止,ExecutorService為我們提供了兩個(gè)方法:shutdown和shutdownNow,這兩個(gè)方法各有不同,可以根據(jù)實(shí)際需求方便的運(yùn)用,如下:

  • shutdown() 平滑的關(guān)閉線程池。(如果還有未執(zhí)行完的任務(wù),就等待它們執(zhí)行完)。
  • shutdownNow() 簡(jiǎn)單粗暴的關(guān)閉線程池。(沒有執(zhí)行完的任務(wù)也直接關(guān)閉)。
  • 線程池的工作流程

    簡(jiǎn)單說:

    • 任務(wù)來了,優(yōu)先考慮核心線程。
    • 核心線程滿了,進(jìn)入阻塞隊(duì)列。
    • 阻塞隊(duì)列滿了,考慮非核心線程。
    • 非核心線程滿了,再觸發(fā)拒絕任務(wù)。

    詳細(xì)說明:

    1 當(dāng)一個(gè)任務(wù)通過submit或者execute方法提交到線程池的時(shí)候,如果當(dāng)前池中線程數(shù)(包括閑置線程)小于coolPoolSize,則創(chuàng)建一個(gè)線程執(zhí)行該任務(wù)。

    2 如果當(dāng)前線程池中線程數(shù)已經(jīng)達(dá)到coolPoolSize,則將任務(wù)放入等待隊(duì)列。

    3 如果任務(wù)不能入隊(duì),說明等待隊(duì)列已滿,若當(dāng)前池中線程數(shù)小于maximumPoolSize,則創(chuàng)建一個(gè)臨時(shí)線程(非核心線程)執(zhí)行該任務(wù)。

    4 如果當(dāng)前池中線程數(shù)已經(jīng)等于maximumPoolSize,此時(shí)無法執(zhí)行該任務(wù),根據(jù)拒絕執(zhí)行策略處理。

    注意:當(dāng)池中線程數(shù)大于coolPoolSize,超過keepAliveTime時(shí)間的閑置線程會(huì)被回收掉?;厥盏氖欠呛诵木€程,核心線程一般是不會(huì)回收的。如果設(shè)置allowCoreThreadTimeOut(true),則核心線程在閑置keepAliveTime時(shí)間后也會(huì)被回收。

    任務(wù)隊(duì)列是一個(gè)阻塞隊(duì)列,線程執(zhí)行完任務(wù)后會(huì)去隊(duì)列取任務(wù)來執(zhí)行,如果隊(duì)列為空,線程就會(huì)阻塞,直到取到任務(wù)。

    其它面試題:

    1.當(dāng)線程池的核心線程數(shù)量過大或者過小的影響?

    首先,多線程編程中一般線程的個(gè)數(shù)都大于CPU核心的個(gè)數(shù),而一個(gè)CPU核心在任意時(shí)刻只能被一個(gè)線程使用,為了讓這些線程都能得到有效的執(zhí)行,CPU采取的策略是為了每個(gè)線程分配時(shí)間片并輪轉(zhuǎn)的形式。當(dāng)一個(gè)線程的時(shí)間片用完的時(shí)候就會(huì)重新處于就緒狀態(tài)讓其他線程使用,這個(gè)過程就屬于一次上下文切換。

    當(dāng)線程池中核心線程數(shù)量過大時(shí),線程與線程之間會(huì)爭(zhēng)取CPU資源,這樣就會(huì)導(dǎo)致上下文切換。過多的上下文切換會(huì)增加線程的執(zhí)行時(shí)間,影響了整體執(zhí)行的效率;

    當(dāng)線程池中的核心線程數(shù)量過少時(shí),如果同一時(shí)間有大量任務(wù)需要處理,可能會(huì)導(dǎo)致大量任務(wù)在任務(wù)隊(duì)列中排隊(duì),甚至?xí)霈F(xiàn)隊(duì)列滿了之后任務(wù)無法執(zhí)行的情況,或者大量任務(wù)堆積在任務(wù)隊(duì)列導(dǎo)致內(nèi)存溢出(OOM)。

    2.CPU密集型和IO密集型?

    CPU密集型:比如加密、解密、壓縮、計(jì)算等一系列需要大量耗費(fèi) CPU 資源的任務(wù)。CPU 密集型任務(wù)應(yīng)配置盡可能小的線程,如配置CPU核數(shù) + 1個(gè)線程的線程池。如果設(shè)置過多的線程數(shù),假設(shè)設(shè)置的線程數(shù)是 CPU 核心數(shù)的 2 倍以上,因?yàn)橛?jì)算任務(wù)非常重,會(huì)占用大量的 CPU 資源,所以這時(shí) CPU 的每個(gè)核心工作基本都是滿負(fù)荷的,而我們又設(shè)置了過多的線程,每個(gè)線程都想去利用 CPU 資源來執(zhí)行自己的任務(wù),這就會(huì)造成不必要的上下文切換,此時(shí)線程數(shù)的增多并沒有讓性能提升,反而由于線程數(shù)量過多會(huì)導(dǎo)致性能下降。

    I/O密集型:比如數(shù)據(jù)庫、文件的讀寫,網(wǎng)絡(luò)通信等任務(wù),這種任務(wù)的特點(diǎn)是并不會(huì)特別消耗 CPU 資源,但是 IO 操作很耗時(shí),總體會(huì)占用比較多的時(shí)間。由于 IO 密集型任務(wù)線程并不是一直在執(zhí)行任務(wù),則應(yīng)配置盡可能多的線程,如CPU核數(shù) * 2。因?yàn)?IO 讀寫速度相比于 CPU 的速度而言是比較慢的,如果我們?cè)O(shè)置過少的線程數(shù),就可能導(dǎo)致 CPU 資源的浪費(fèi)。而如果我們?cè)O(shè)置更多的線程數(shù),那么當(dāng)一部分線程正在等待 IO 的時(shí)候,它們此時(shí)并不需要 CPU 來計(jì)算,那么另外的線程便可以利用 CPU 去執(zhí)行其他的任務(wù),互不影響,這樣的話在工作隊(duì)列中等待的任務(wù)就會(huì)減少,可以更好地利用資源。

    混合型任務(wù):既包含CPU密集型又包含I/O密集型。

    3.corePoolSize核心線程數(shù),一般設(shè)置為多少?

    首先要考慮到 CPU 核心數(shù);可以使用下面的方法獲取

    Runtime.getRuntime().availableProcessor() 方法來獲取(可能不準(zhǔn)確,作為參考)

    在確認(rèn)了核心數(shù)后,再去判斷是 CPU 密集型任務(wù)還是 IO 密集型任務(wù)

    • CPU密集型:核心線程數(shù) = CPU核數(shù) + 1
    • IO密集型:核心線程數(shù) = CPU核數(shù) * 2

    注:IO密集型(某大廠實(shí)踐經(jīng)驗(yàn))

    核心線程數(shù) = CPU核數(shù) / (1-阻塞系數(shù))

    其中計(jì)算密集型阻塞系數(shù)為 0,IO 密集型阻塞系數(shù)接近 1,一般認(rèn)為在 0.8 ~ 0.9 之間。比如 8 核
    CPU,按照公式就是 2 / ( 1 - 0.9 ) = 20 個(gè)線程數(shù)

    另外,可參考AsyncTask源碼中的設(shè)置:

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();// We want at least 2 threads and at most 4 threads in the core pool,// preferring to have 1 less than the CPU count to avoid saturating// the CPU with background workprivate static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

    源碼有解釋的:之所以 減掉這個(gè)1,是因?yàn)闉榱吮苊夂笈_(tái)任務(wù)將 CPU 資源完全耗盡, 減掉的這個(gè)1 是留給我們 主線程 使用的。

    1.6. Java鎖機(jī)制

    在java中,解決同步問題,很多時(shí)候都會(huì)使用到synchronizedLock,這兩者都是在多線程并發(fā)時(shí)候常使用的鎖機(jī)制。在JDK1.6后,對(duì)synchronized進(jìn)行了很多優(yōu)化,如偏向鎖、輕量級(jí)鎖等,synchronized的性能已經(jīng)與Reentrantlock大致相同,除非要使用Reentrantlock的一些高級(jí)功能(實(shí)現(xiàn)公平鎖、中斷鎖等),一般推薦使用synchronized關(guān)鍵字來實(shí)現(xiàn)加鎖機(jī)制。

    Synchronized 是Java 并發(fā)編程中很重要的關(guān)鍵字,另外一個(gè)很重要的是 volatile。Syncronized 一次只允許一個(gè)線程進(jìn)入由他修飾的代碼段,從而允許他們進(jìn)行自我保護(hù)。進(jìn)入由Synchronized 保護(hù)的代碼區(qū)首先需要獲取 Synchronized 這把鎖,其他線程想要執(zhí)行必須進(jìn)行等待。Synchronized 鎖住的代碼區(qū)域執(zhí)行完成后需要把鎖歸還,也就是釋放鎖,這樣才能夠讓其他線程使用。

    Lock 是 Java并發(fā)編程中很重要的一個(gè)接口,它要比 Synchronized 關(guān)鍵字更能直譯"鎖"的概念,Lock需要手動(dòng)加鎖和手動(dòng)解鎖,一般通過 lock.lock() 方法來進(jìn)行加鎖, 通過 lock.unlock() 方法進(jìn)行解鎖。與 Lock 關(guān)聯(lián)密切的鎖有 ReetrantLockReadWriteLock。

    ReetrantLock 實(shí)現(xiàn)了Lock接口,它是一個(gè)可重入鎖,內(nèi)部定義了公平鎖與非公平鎖。

    ReadWriteLock 一個(gè)用來獲取讀鎖,一個(gè)用來獲取寫鎖。也就是說將文件的讀寫操作分開,分成2個(gè)鎖來分配給線程,從而使得多個(gè)線程可以同時(shí)進(jìn)行讀操作。ReentrantReadWirteLock實(shí)現(xiàn)了ReadWirteLock接口,并未實(shí)現(xiàn)Lock接口。

    Synchronized 的使用

    修飾一個(gè)方法:即一次只能有一個(gè)線程進(jìn)入該方法,其他線程要想在此時(shí)調(diào)用該方法,只能排隊(duì)等候。

    實(shí)例方法:鎖住的是該類的實(shí)例對(duì)象 靜態(tài)方法:鎖住的是該類的類對(duì)象。public synchronized void goHOme(){ }public static synchronized void goHOme(){ }

    修飾代碼塊:表示只能有一個(gè)線程進(jìn)入某個(gè)代碼段

    public void numDecrease(Object num){synchronized (num){number++;} }

    修飾一個(gè)類:作用的對(duì)象是這個(gè)類的所有對(duì)象,只要是這個(gè)類型的class不管有幾個(gè)對(duì)象都會(huì)起作用。

    class Person {public void method() {//鎖住的是該類的類對(duì)象,如果換成this或其他object,則鎖住的是該類的實(shí)例對(duì)象synchronized(Person.class) {// todo}}}

    獲取對(duì)象鎖

    synchronized(this|object) {} 修飾非靜態(tài)方法

    獲取類鎖

    synchronized(類.class) {} 修飾靜態(tài)方法

    Lock 的使用

    public interface Lock {void lock();void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit unit) throws InterruptedException; void unlock(); Condition newCondition(); }


    使用示例:

    Lock lock = ...; lock.lock(); try{//處理任務(wù) }catch(Exception ex){}finally{lock.unlock(); //釋放鎖 }Lock lock = ...; if(lock.tryLock()) {try{//處理任務(wù)}catch(Exception ex){}finally{lock.unlock(); //釋放鎖} }else {//如果不能獲取鎖,則直接做其他事情 }public void method() throws InterruptedException {lock.lockInterruptibly();try {//.....}finally {lock.unlock();} }

    一般來說,使用Lock必須在try{}catch{}塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行,以保證鎖一定被被釋放,防止死鎖的發(fā)生。

    注意,當(dāng)一個(gè)線程獲取了鎖之后,是不會(huì)被interrupt()方法中斷的。單獨(dú)調(diào)用interrupt()方法不能中斷正在運(yùn)行過程中的線程,只能中斷阻塞過程中的線程。因此當(dāng)通過lockInterruptibly()方法獲取某個(gè)鎖時(shí),如果不能獲取到,只有進(jìn)行等待的情況下,是可以響應(yīng)中斷的。而用synchronized修飾的話,當(dāng)一個(gè)線程處于等待某個(gè)鎖的狀態(tài),是無法被中斷的,只有一直等待下去。

    synchronized和Lock的區(qū)別?

    主要區(qū)別:

    synchronized是Java中的關(guān)鍵字,是Java的內(nèi)置實(shí)現(xiàn);Lock是Java中的接口。 synchronized遇到異常會(huì)釋放鎖;Lock需要在發(fā)生異常的時(shí)候調(diào)用成員方法Lock#unlock()方法。 synchronized是不可以中斷的,Lock可中斷。 synchronized不能去嘗試獲得鎖,沒有獲得鎖就會(huì)被阻塞; Lock可以去嘗試獲得鎖,如果未獲得可以嘗試處理其他邏輯。 synchronized多線程效率不如Lock,不過Java在1.6以后已經(jīng)對(duì)synchronized進(jìn)行大量的優(yōu)化,所以性能上來講,其實(shí)差不了多少。

    死鎖

    所謂死鎖是指兩個(gè)或兩個(gè)以上的線程在執(zhí)行過程中,因爭(zhēng)奪資源而造成的一種互相等待的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。

    死鎖觸發(fā)的四大條件?

    互斥鎖 請(qǐng)求與保持 不可剝奪 循環(huán)的請(qǐng)求與等待

    簡(jiǎn)單死鎖代碼示例:

    public class DeadLock {public static String obj1 = "obj1";public static String obj2 = "obj2";public static void main(String[] args){Thread a = new Thread(new Lock1());Thread b = new Thread(new Lock2());a.start();b.start();} } class Lock1 implements Runnable{@Overridepublic void run(){try{System.out.println("Lock1 running");while(true){synchronized(DeadLock.obj1){System.out.println("Lock1 lock obj1");Thread.sleep(3000);//獲取obj1后先等一會(huì)兒,讓Lock2有足夠的時(shí)間鎖住obj2synchronized(DeadLock.obj2){System.out.println("Lock1 lock obj2");}}}}catch(Exception e){e.printStackTrace();}} } class Lock2 implements Runnable{@Overridepublic void run(){try{System.out.println("Lock2 running");while(true){synchronized(DeadLock.obj2){System.out.println("Lock2 lock obj2");Thread.sleep(3000);synchronized(DeadLock.obj1){System.out.println("Lock2 lock obj1");}}}}catch(Exception e){e.printStackTrace();}} }


    可以看到,Lock1獲取obj1,Lock2獲取obj2,但是它們都沒有辦法再獲取另外一個(gè)obj,因?yàn)樗鼈兌荚诘却龑?duì)方先釋放鎖,這時(shí)就是死鎖。

    1.7. Java中的主流鎖

    Java中往往是按照是否含有某一特性來定義鎖,我們通過特性將鎖進(jìn)行分組歸類:


    1.樂觀鎖 VS 悲觀鎖
    概念:對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,樂觀鎖認(rèn)為自己在使用數(shù)據(jù)時(shí)不會(huì)有別的線程修改數(shù)據(jù),所以不會(huì)添加鎖,只是在更新數(shù)據(jù)的時(shí)候去判斷之前有沒有別的線程更新了這個(gè)數(shù)據(jù)。如果這個(gè)數(shù)據(jù)沒有被更新,當(dāng)前線程將自己修改的數(shù)據(jù)成功寫入。如果數(shù)據(jù)已經(jīng)被其他線程更新,則根據(jù)不同的實(shí)現(xiàn)方式執(zhí)行不同的操作(例如報(bào)錯(cuò)或者自動(dòng)重試)。

    悲觀鎖認(rèn)為自己在使用數(shù)據(jù)的時(shí)候一定有別的線程來修改數(shù)據(jù),因此在獲取數(shù)據(jù)的時(shí)候會(huì)先加鎖,確保數(shù)據(jù)不會(huì)被別的線程修改。Java中,synchronized關(guān)鍵字和Lock的實(shí)現(xiàn)類都是悲觀鎖。

    樂觀鎖在Java中是通過使用無鎖編程來實(shí)現(xiàn),最常采用的是CAS算法,Java原子類中的遞增操作就通過CAS自旋實(shí)現(xiàn)的。

    根據(jù)從上面的概念描述我們可以發(fā)現(xiàn):

    • 悲觀鎖適合寫操作多的場(chǎng)景,先加鎖可以保證寫操作時(shí)數(shù)據(jù)正確。
    • 樂觀鎖適合讀操作多的場(chǎng)景,不加鎖的特點(diǎn)能夠使其讀操作的性能大幅提升。

    調(diào)用方式示例:

    悲觀鎖基本都是在顯式的鎖定之后再操作同步資源,而樂觀鎖則直接去操作同步資源。那么,為何樂觀鎖能夠做到不鎖定同步資源也可以正確的實(shí)現(xiàn)線程同步呢?

    樂觀鎖的主要實(shí)現(xiàn)方式 “CAS” 的技術(shù)原理:
    CAS全稱 Compare And Swap(比較與交換),是一種無鎖算法。在不使用鎖(沒有線程被阻塞)的情況下實(shí)現(xiàn)多線程之間的變量同步。java.util.concurrent包中的原子類就是通過CAS來實(shí)現(xiàn)了樂觀鎖。

    CAS算法涉及到三個(gè)操作數(shù):

    • 需要讀寫的內(nèi)存值 V。
    • 進(jìn)行比較的值 A。
    • 要寫入的新值 B。

    當(dāng)且僅當(dāng) V 的值等于 A 時(shí),CAS通過原子方式用新值B來更新V的值(“比較+更新”整體是一個(gè)原子操作),否則不會(huì)執(zhí)行任何操作。一般情況下,“更新”是一個(gè)不斷重試的操作。

    之前提到j(luò)ava.util.concurrent包中的原子類,就是通過CAS來實(shí)現(xiàn)了樂觀鎖,那么我們進(jìn)入原子類AtomicInteger的源碼,看一下AtomicInteger的定義:


    根據(jù)定義我們可以看出各屬性的作用:

    • unsafe: 獲取并操作內(nèi)存的數(shù)據(jù)。
    • valueOffset: 存儲(chǔ)value在AtomicInteger中的偏移量。
    • value: 存儲(chǔ)AtomicInteger的int值,該屬性需要借助volatile關(guān)鍵字保證其在線程間是可見的。

    接下來,我們查看AtomicInteger的自增函數(shù)incrementAndGet()的源碼時(shí),發(fā)現(xiàn)自增函數(shù)底層調(diào)用的是unsafe.getAndAddInt()。但是由于JDK本身只有Unsafe.class,只通過class文件中的參數(shù)名,并不能很好的了解方法的作用,所以我們通過OpenJDK 8 來查看Unsafe的源碼:

    根據(jù)OpenJDK 8的源碼我們可以看出,getAndAddInt()循環(huán)獲取給定對(duì)象o中的偏移量處的值v,然后判斷內(nèi)存值是否等于v。如果相等則將內(nèi)存值設(shè)置為 v + delta,否則返回false,繼續(xù)循環(huán)進(jìn)行重試,直到設(shè)置成功才能退出循環(huán),并且將舊值返回。整個(gè)“比較+更新”操作封裝在compareAndSwapInt()中,在JNI里是借助于一個(gè)CPU指令完成的,屬于原子操作,可以保證多個(gè)線程都能夠看到同一個(gè)變量的修改值。

    后續(xù)JDK通過CPU的cmpxchg指令,去比較寄存器中的 A 和 內(nèi)存中的值 V。如果相等,就把要寫入的新值 B 存入內(nèi)存中。如果不相等,就將內(nèi)存值 V 賦值給寄存器中的值 A。然后通過Java代碼中的while循環(huán)再次調(diào)用cmpxchg指令進(jìn)行重試,直到設(shè)置成功為止。

    CAS雖然很高效,但是它也存在三大問題,這里也簡(jiǎn)單說一下:

  • ABA問題。CAS需要在操作值的時(shí)候檢查內(nèi)存值是否發(fā)生變化,沒有發(fā)生變化才會(huì)更新內(nèi)存值。但是如果內(nèi)存值原來是A,后來變成了B,然后又變成了A,那么CAS進(jìn)行檢查時(shí)會(huì)發(fā)現(xiàn)值沒有發(fā)生變化,但是實(shí)際上是有變化的。ABA問題的解決思路就是在變量前面添加版本號(hào),每次變量更新的時(shí)候都把版本號(hào)加一,這樣變化過程就從“A-B-A”變成了“1A-2B-3A”。

    JDK從1.5開始提供了AtomicStampedReference類來解決ABA問題,具體操作封裝在compareAndSet()中。compareAndSet()首先檢查當(dāng)前引用和當(dāng)前標(biāo)志與預(yù)期引用和預(yù)期標(biāo)志是否相等,如果都相等,則以原子方式將引用值和標(biāo)志的值設(shè)置為給定的更新值。

  • 循環(huán)時(shí)間長(zhǎng)開銷大。CAS操作如果長(zhǎng)時(shí)間不成功,會(huì)導(dǎo)致其一直自旋,給CPU帶來非常大的開銷。

  • 只能保證一個(gè)共享變量的原子操作。對(duì)一個(gè)共享變量執(zhí)行操作時(shí),CAS能夠保證原子操作,但是對(duì)多個(gè)共享變量操作時(shí),CAS是無法保證操作的原子性的。

  • Java從1.5開始JDK提供了AtomicReference類來保證引用對(duì)象之間的原子性,可以把多個(gè)變量放在一個(gè)對(duì)象里來進(jìn)行CAS操作。

    2.自旋鎖 VS 適應(yīng)性自旋鎖

    概念解析:

    阻塞或喚醒一個(gè)Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成,這種狀態(tài)轉(zhuǎn)換需要耗費(fèi)處理器時(shí)間。如果同步代碼塊中的內(nèi)容過于簡(jiǎn)單,狀態(tài)轉(zhuǎn)換消耗的時(shí)間有可能比用戶代碼執(zhí)行的時(shí)間還要長(zhǎng)。

    在許多場(chǎng)景中,同步資源的鎖定時(shí)間很短,為了這一小段時(shí)間去切換線程,線程掛起和恢復(fù)現(xiàn)場(chǎng)的花費(fèi)可能會(huì)讓系統(tǒng)得不償失。如果物理機(jī)器有多個(gè)處理器,能夠讓兩個(gè)或以上的線程同時(shí)并行執(zhí)行,我們就可以讓后面那個(gè)請(qǐng)求鎖的線程不放棄CPU的執(zhí)行時(shí)間,看看持有鎖的線程是否很快就會(huì)釋放鎖。

    而為了讓當(dāng)前線程“稍等一下”,我們需讓當(dāng)前線程進(jìn)行自旋,如果在自旋完成后前面鎖定同步資源的線程已經(jīng)釋放了鎖,那么當(dāng)前線程就可以不必阻塞而是直接獲取同步資源,從而避免切換線程的開銷。這就是自旋鎖。

    自旋鎖本身是有缺點(diǎn)的,它不能代替阻塞。自旋等待雖然避免了線程切換的開銷,但它要占用處理器時(shí)間。如果鎖被占用的時(shí)間很短,自旋等待的效果就會(huì)非常好。反之,如果鎖被占用的時(shí)間很長(zhǎng),那么自旋的線程只會(huì)白浪費(fèi)處理器資源。所以,自旋等待的時(shí)間必須要有一定的限度,如果自旋超過了限定次數(shù)(默認(rèn)是10次,可以使用-XX:PreBlockSpin來更改)沒有成功獲得鎖,就應(yīng)當(dāng)掛起線程。

    自旋鎖的實(shí)現(xiàn)原理同樣也是CAS,AtomicInteger中調(diào)用unsafe進(jìn)行自增操作的源碼中的do-while循環(huán)就是一個(gè)自旋操作,如果修改數(shù)值失敗則通過循環(huán)來執(zhí)行自旋,直至修改成功。


    自旋鎖在JDK1.4.2中引入,使用-XX:+UseSpinning來開啟。JDK 6中變?yōu)槟J(rèn)開啟,并且引入了自適應(yīng)的自旋鎖(適應(yīng)性自旋鎖)。

    自適應(yīng)意味著自旋的時(shí)間(次數(shù))不再固定,而是由前一次在同一個(gè)鎖上的自旋時(shí)間及鎖的擁有者的狀態(tài)來決定。如果在同一個(gè)鎖對(duì)象上,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運(yùn)行中,那么虛擬機(jī)就會(huì)認(rèn)為這次自旋也是很有可能再次成功,進(jìn)而它將允許自旋等待持續(xù)相對(duì)更長(zhǎng)的時(shí)間。如果對(duì)于某個(gè)鎖,自旋很少成功獲得過,那在以后嘗試獲取這個(gè)鎖時(shí)將可能省略掉自旋過程,直接阻塞線程,避免浪費(fèi)處理器資源。

    在自旋鎖中另有三種常見的鎖形式:TicketLock、CLHlock和MCSlock,本文中僅做名詞介紹,不做深入講解,感興趣的同學(xué)可以自行查閱相關(guān)資料。

    3.無鎖 VS 偏向鎖 VS 輕量級(jí)鎖 VS 重量級(jí)鎖

    這四種鎖是指鎖的狀態(tài),專門針對(duì)synchronized的。在介紹這四種鎖狀態(tài)之前還需要介紹一些額外的知識(shí)。

    首先為什么Synchronized能實(shí)現(xiàn)線程同步?

    在回答這個(gè)問題之前我們需要了解兩個(gè)重要的概念:“Java對(duì)象頭”、“Monitor”。

    Java對(duì)象頭
    synchronized是悲觀鎖,在操作同步資源之前需要給同步資源先加鎖,這把鎖就是存在Java對(duì)象頭里的,而Java對(duì)象頭又是什么呢?

    我們以Hotspot虛擬機(jī)為例,Hotspot的對(duì)象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Klass Pointer(類型指針)。

    Mark Word:默認(rèn)存儲(chǔ)對(duì)象的HashCode,分代年齡和鎖標(biāo)志位信息。這些信息都是與對(duì)象自身定義無關(guān)的數(shù)據(jù),所以Mark Word被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲(chǔ)盡量多的數(shù)據(jù)。它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間,也就是說在運(yùn)行期間Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。

    Klass Point:對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。

    Monitor
    Monitor可以理解為一個(gè)同步工具或一種同步機(jī)制,通常被描述為一個(gè)對(duì)象。每一個(gè)Java對(duì)象就有一把看不見的鎖,稱為內(nèi)部鎖或者M(jìn)onitor鎖。

    Monitor是線程私有的數(shù)據(jù)結(jié)構(gòu),每一個(gè)線程都有一個(gè)可用monitor record列表,同時(shí)還有一個(gè)全局的可用列表。每一個(gè)被鎖住的對(duì)象都會(huì)和一個(gè)monitor關(guān)聯(lián),同時(shí)monitor中有一個(gè)Owner字段存放擁有該鎖的線程的唯一標(biāo)識(shí),表示該鎖被這個(gè)線程占用。

    現(xiàn)在話題回到synchronized,synchronized通過Monitor來實(shí)現(xiàn)線程同步,Monitor是依賴于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來實(shí)現(xiàn)的線程同步。

    如同我們?cè)谧孕i中提到的“阻塞或喚醒一個(gè)Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成,這種狀態(tài)轉(zhuǎn)換需要耗費(fèi)處理器時(shí)間。如果同步代碼塊中的內(nèi)容過于簡(jiǎn)單,狀態(tài)轉(zhuǎn)換消耗的時(shí)間有可能比用戶代碼執(zhí)行的時(shí)間還要長(zhǎng)”。這種方式就是synchronized最初實(shí)現(xiàn)同步的方式,這就是JDK 6之前synchronized效率低的原因。這種依賴于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱之為“重量級(jí)鎖”,JDK 6中為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”。

    所以目前鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無鎖、偏向鎖、輕量級(jí)鎖和重量級(jí)鎖。鎖狀態(tài)只能升級(jí)不能降級(jí)。

    通過上面的介紹,我們對(duì)synchronized的加鎖機(jī)制以及相關(guān)知識(shí)有了一個(gè)了解,那么下面我們給出四種鎖狀態(tài)對(duì)應(yīng)的的Mark Word內(nèi)容,然后再分別講解四種鎖狀態(tài)的思路以及特點(diǎn):

    無鎖

    無鎖沒有對(duì)資源進(jìn)行鎖定,所有的線程都能訪問并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功。

    無鎖的特點(diǎn)就是修改操作在循環(huán)內(nèi)進(jìn)行,線程會(huì)不斷的嘗試修改共享資源。如果沒有沖突就修改成功并退出,否則就會(huì)繼續(xù)循環(huán)嘗試。如果有多個(gè)線程修改同一個(gè)值,必定會(huì)有一個(gè)線程能修改成功,而其他修改失敗的線程會(huì)不斷重試直到修改成功。上面我們介紹的CAS原理及應(yīng)用即是無鎖的實(shí)現(xiàn)。無鎖無法全面代替有鎖,但無鎖在某些場(chǎng)合下的性能是非常高的。

    偏向鎖

    偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問,那么該線程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)。

    在大多數(shù)情況下,鎖總是由同一線程多次獲得,不存在多線程競(jìng)爭(zhēng),所以出現(xiàn)了偏向鎖。其目標(biāo)就是在只有一個(gè)線程執(zhí)行同步代碼塊時(shí)能夠提高性能。

    當(dāng)一個(gè)線程訪問同步代碼塊并獲取鎖時(shí),會(huì)在Mark Word里存儲(chǔ)鎖偏向的線程ID。在線程進(jìn)入和退出同步塊時(shí)不再通過CAS操作來加鎖和解鎖,而是檢測(cè)Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。引入偏向鎖是為了在無多線程競(jìng)爭(zhēng)的情況下盡量減少不必要的輕量級(jí)鎖執(zhí)行路徑,因?yàn)檩p量級(jí)鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時(shí)候依賴一次CAS原子指令即可。

    偏向鎖只有遇到其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖,線程不會(huì)主動(dòng)釋放偏向鎖。偏向鎖的撤銷,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒有字節(jié)碼正在執(zhí)行),它會(huì)首先暫停擁有偏向鎖的線程,判斷鎖對(duì)象是否處于被鎖定狀態(tài)。撤銷偏向鎖后恢復(fù)到無鎖(標(biāo)志位為“01”)或輕量級(jí)鎖(標(biāo)志位為“00”)的狀態(tài)。

    偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的。可以通過JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。

    輕量級(jí)鎖

    是指當(dāng)鎖是偏向鎖的時(shí)候,被另外的線程所訪問,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能。

    在代碼進(jìn)入同步塊的時(shí)候,如果同步對(duì)象鎖狀態(tài)為無鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前的Mark Word的拷貝,然后拷貝對(duì)象頭中的Mark Word復(fù)制到鎖記錄中。

    拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對(duì)象的Mark Word更新為指向Lock Record的指針,并將Lock Record里的owner指針指向?qū)ο蟮腗ark Word。

    如果這個(gè)更新動(dòng)作成功了,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位設(shè)置為“00”,表示此對(duì)象處于輕量級(jí)鎖定狀態(tài)。

    如果輕量級(jí)鎖的更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行,否則說明多個(gè)線程競(jìng)爭(zhēng)鎖。

    若當(dāng)前只有一個(gè)等待線程,則該線程通過自旋進(jìn)行等待。但是當(dāng)自旋超過一定的次數(shù),或者一個(gè)線程在持有鎖,一個(gè)在自旋,又有第三個(gè)來訪時(shí),輕量級(jí)鎖升級(jí)為重量級(jí)鎖。

    重量級(jí)鎖

    升級(jí)為重量級(jí)鎖時(shí),鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”,此時(shí)Mark Word中存儲(chǔ)的是指向重量級(jí)鎖的指針,此時(shí)等待鎖的線程都會(huì)進(jìn)入阻塞狀態(tài)。

    整體的鎖狀態(tài)升級(jí)流程如下:

    綜上,偏向鎖通過對(duì)比Mark Word解決加鎖問題,避免執(zhí)行CAS操作。而輕量級(jí)鎖是通過用CAS操作和自旋來解決加鎖問題,避免線程阻塞和喚醒而影響性能。重量級(jí)鎖是將除了擁有鎖的線程以外的線程都阻塞。

    4. 公平鎖 VS 非公平鎖

    公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來獲取鎖,線程直接進(jìn)入隊(duì)列中排隊(duì),隊(duì)列中的第一個(gè)線程才能獲得鎖。公平鎖的優(yōu)點(diǎn)是等待鎖的線程不會(huì)餓死,缺點(diǎn)是整體吞吐效率相對(duì)非公平鎖要低,等待隊(duì)列中除第一個(gè)線程以外的所有線程都會(huì)阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。

    非公平鎖是多個(gè)線程加鎖時(shí)直接嘗試獲取鎖,獲取不到才會(huì)到等待隊(duì)列的隊(duì)尾等待。但如果此時(shí)鎖剛好可用,那么這個(gè)線程可以無需阻塞直接獲取到鎖,所以非公平鎖有可能出現(xiàn)后申請(qǐng)鎖的線程先獲取鎖的場(chǎng)景。非公平鎖的優(yōu)點(diǎn)是可以減少喚起線程的開銷,整體的吞吐效率高,因?yàn)榫€程有幾率不阻塞直接獲得鎖,CPU不必喚醒所有線程。缺點(diǎn)是處于等待隊(duì)列中的線程可能會(huì)餓死,或者等很久才會(huì)獲得鎖。

    直接用語言描述可能有點(diǎn)抽象,這里用一個(gè)例子來講述一下公平鎖和非公平鎖。

    如上圖所示,假設(shè)有一口水井,有管理員看守,管理員有一把鎖,只有拿到鎖的人才能夠打水,打完水要把鎖還給管理員。每個(gè)過來打水的人都要管理員的允許并拿到鎖之后才能去打水,如果前面有人正在打水,那么這個(gè)想要打水的人就必須排隊(duì)。管理員會(huì)查看下一個(gè)要去打水的人是不是隊(duì)伍里排最前面的人,如果是的話,才會(huì)給你鎖讓你去打水;如果你不是排第一的人,就必須去隊(duì)尾排隊(duì),這就是公平鎖。
    但是對(duì)于非公平鎖,管理員對(duì)打水的人沒有要求。即使等待隊(duì)伍里有排隊(duì)等待的人,但如果在上一個(gè)人剛打完水把鎖還給管理員而且管理員還沒有允許等待隊(duì)伍里下一個(gè)人去打水時(shí),剛好來了一個(gè)插隊(duì)的人,這個(gè)插隊(duì)的人是可以直接從管理員那里拿到鎖去打水,不需要排隊(duì),原本排隊(duì)等待的人只能繼續(xù)等待。如下圖所示


    接下來我們通過ReentrantLock的源碼來講解公平鎖和非公平鎖。


    根據(jù)代碼可知,ReentrantLock里面有一個(gè)內(nèi)部類Sync,Sync繼承AQS(AbstractQueuedSynchronizer),添加鎖和釋放鎖的大部分操作實(shí)際上都是在Sync中實(shí)現(xiàn)的。它有公平鎖FairSync和非公平鎖NonfairSync兩個(gè)子類。ReentrantLock默認(rèn)使用非公平鎖,也可以通過構(gòu)造器來顯示的指定使用公平鎖。
    下面我們來看一下公平鎖與非公平鎖的加鎖方法的源碼:


    通過上圖中的源代碼對(duì)比,我們可以明顯的看出公平鎖與非公平鎖的lock()方法唯一的區(qū)別就在于公平鎖在獲取同步狀態(tài)時(shí)多了一個(gè)限制條件:hasQueuedPredecessors()。

    再進(jìn)入hasQueuedPredecessors(),可以看到該方法主要做一件事情:主要是判斷當(dāng)前線程是否位于同步隊(duì)列中的第一個(gè)。如果是則返回true,否則返回false。
    綜上,公平鎖就是通過同步隊(duì)列來實(shí)現(xiàn)多個(gè)線程按照申請(qǐng)鎖的順序來獲取鎖,從而實(shí)現(xiàn)公平的特性。非公平鎖加鎖時(shí)不考慮排隊(duì)等待問題,直接嘗試獲取鎖,所以存在后申請(qǐng)卻先獲得鎖的情況。

    5. 可重入鎖 VS 非可重入鎖

    可重入鎖又名遞歸鎖,是指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,再進(jìn)入該線程的內(nèi)層方法會(huì)自動(dòng)獲取鎖(前提鎖對(duì)象得是同一個(gè)對(duì)象或者class),不會(huì)因?yàn)橹耙呀?jīng)獲取過還沒釋放而阻塞。Java中ReentrantLock和synchronized都是可重入鎖,可重入鎖的一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖。下面用示例代碼來進(jìn)行分析:


    在上面的代碼中,類中的兩個(gè)方法都是被內(nèi)置鎖synchronized修飾的,doSomething()方法中調(diào)用doOthers()方法。因?yàn)閮?nèi)置鎖是可重入的,所以同一個(gè)線程在調(diào)用doOthers()時(shí)可以直接獲得當(dāng)前對(duì)象的鎖,進(jìn)入doOthers()進(jìn)行操作。

    如果是一個(gè)不可重入鎖,那么當(dāng)前線程在調(diào)用doOthers()之前需要將執(zhí)行doSomething()時(shí)獲取當(dāng)前對(duì)象的鎖釋放掉,實(shí)際上該對(duì)象鎖已被當(dāng)前線程所持有,且無法釋放。所以此時(shí)會(huì)出現(xiàn)死鎖。

    而為什么可重入鎖就可以在嵌套調(diào)用時(shí)可以自動(dòng)獲得鎖呢?我們通過圖示和源碼來分別解析一下。

    還是打水的例子,有多個(gè)人在排隊(duì)打水,此時(shí)管理員允許鎖和同一個(gè)人的多個(gè)水桶綁定。這個(gè)人用多個(gè)水桶打水時(shí),第一個(gè)水桶和鎖綁定并打完水之后,第二個(gè)水桶也可以直接和鎖綁定并開始打水,所有的水桶都打完水之后打水人才會(huì)將鎖還給管理員。這個(gè)人的所有打水流程都能夠成功執(zhí)行,后續(xù)等待的人也能夠打到水。這就是可重入鎖。


    但如果是非可重入鎖的話,此時(shí)管理員只允許鎖和同一個(gè)人的一個(gè)水桶綁定。第一個(gè)水桶和鎖綁定打完水之后并不會(huì)釋放鎖,導(dǎo)致第二個(gè)水桶不能和鎖綁定也無法打水。當(dāng)前線程出現(xiàn)死鎖,整個(gè)等待隊(duì)列中的所有線程都無法被喚醒。


    之前我們說過ReentrantLock和synchronized都是重入鎖,那么我們通過重入鎖ReentrantLock以及非可重入鎖NonReentrantLock的源碼來對(duì)比分析一下為什么非可重入鎖在重復(fù)調(diào)用同步資源時(shí)會(huì)出現(xiàn)死鎖。
    首先ReentrantLock和NonReentrantLock都繼承父類AQS,其父類AQS中維護(hù)了一個(gè)同步狀態(tài)status來計(jì)數(shù)重入次數(shù),status初始值為0。

    當(dāng)線程嘗試獲取鎖時(shí),可重入鎖先嘗試獲取并更新status值,如果status == 0表示沒有其他線程在執(zhí)行同步代碼,則把status置為1,當(dāng)前線程開始執(zhí)行。如果status != 0,則判斷當(dāng)前線程是否是獲取到這個(gè)鎖的線程,如果是的話執(zhí)行status+1,且當(dāng)前線程可以再次獲取鎖。而非可重入鎖是直接去獲取并嘗試更新當(dāng)前status的值,如果status != 0的話會(huì)導(dǎo)致其獲取鎖失敗,當(dāng)前線程阻塞。

    釋放鎖時(shí),可重入鎖同樣先獲取當(dāng)前status的值,在當(dāng)前線程是持有鎖的線程的前提下。如果status-1 == 0,則表示當(dāng)前線程所有重復(fù)獲取鎖的操作都已經(jīng)執(zhí)行完畢,然后該線程才會(huì)真正釋放鎖。而非可重入鎖則是在確定當(dāng)前線程是持有鎖的線程之后,直接將status置為0,將鎖釋放。


    6. 獨(dú)享鎖 VS 共享鎖

    獨(dú)享鎖和共享鎖同樣是一種概念。我們先介紹一下具體的概念,然后通過ReentrantLock和ReentrantReadWriteLock的源碼來介紹獨(dú)享鎖和共享鎖。

    獨(dú)享鎖也叫排他鎖,是指該鎖一次只能被一個(gè)線程所持有。如果線程T對(duì)數(shù)據(jù)A加上排它鎖后,則其他線程不能再對(duì)A加任何類型的鎖。獲得排它鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。JDK中的synchronized和JUC中Lock的實(shí)現(xiàn)類就是互斥鎖。

    共享鎖是指該鎖可被多個(gè)線程所持有。如果線程T對(duì)數(shù)據(jù)A加上共享鎖后,則其他線程只能對(duì)A再加共享鎖,不能加排它鎖。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)。

    獨(dú)享鎖與共享鎖也是通過AQS來實(shí)現(xiàn)的,通過實(shí)現(xiàn)不同的方法,來實(shí)現(xiàn)獨(dú)享或者共享。

    下圖為ReentrantReadWriteLock的部分源碼:

    我們看到ReentrantReadWriteLock有兩把鎖:ReadLock和WriteLock,由詞知意,一個(gè)讀鎖一個(gè)寫鎖,合稱“讀寫鎖”。再進(jìn)一步觀察可以發(fā)現(xiàn)ReadLock和WriteLock是靠?jī)?nèi)部類Sync實(shí)現(xiàn)的鎖。Sync是AQS的一個(gè)子類,這種結(jié)構(gòu)在CountDownLatch、ReentrantLock、Semaphore里面也都存在。
    在ReentrantReadWriteLock里面,讀鎖和寫鎖的鎖主體都是Sync,但讀鎖和寫鎖的加鎖方式不一樣。讀鎖是共享鎖,寫鎖是獨(dú)享鎖。讀鎖的共享鎖可保證并發(fā)讀非常高效,而讀寫、寫讀、寫寫的過程互斥,因?yàn)樽x鎖和寫鎖是分離的。所以ReentrantReadWriteLock的并發(fā)性相比一般的互斥鎖有了很大提升。

    那讀鎖和寫鎖的具體加鎖方式有什么區(qū)別呢?在了解源碼之前我們需要回顧一下其他知識(shí)。

    在最開始提及AQS的時(shí)候我們也提到了state字段(int類型,32位),該字段用來描述有多少線程獲持有鎖。

    在獨(dú)享鎖中這個(gè)值通常是0或者1(如果是重入鎖的話state值就是重入的次數(shù)),在共享鎖中state就是持有鎖的數(shù)量。但是在ReentrantReadWriteLock中有讀、寫兩把鎖,所以需要在一個(gè)整型變量state上分別描述讀鎖和寫鎖的數(shù)量(或者也可以叫狀態(tài))。于是將state變量“按位切割”切分成了兩個(gè)部分,高16位表示讀鎖狀態(tài)(讀鎖個(gè)數(shù)),低16位表示寫鎖狀態(tài)(寫鎖個(gè)數(shù))。如下圖所示:

    了解了概念之后我們?cè)賮砜创a,先看寫鎖的加鎖源碼:

    • 這段代碼首先取到當(dāng)前鎖的個(gè)數(shù)c,然后再通過c來獲取寫鎖的個(gè)數(shù)w。因?yàn)閷戞i是低16位,所以取低16位的最大值與當(dāng)前的c做與運(yùn)算( int w
      = exclusiveCount; ),高16位和0與運(yùn)算后是0,剩下的就是低位運(yùn)算的值,同時(shí)也是持有寫鎖的線程數(shù)目。
    • 在取到寫鎖線程的數(shù)目后,首先判斷是否已經(jīng)有線程持有了鎖。如果已經(jīng)有線程持有了鎖(c!=0),則查看當(dāng)前寫鎖線程的數(shù)目,如果寫線程數(shù)為0(即此時(shí)存在讀鎖)或者持有鎖的線程不是當(dāng)前線程就返回失敗(涉及到公平鎖和非公平鎖的實(shí)現(xiàn))。
    • 如果寫入鎖的數(shù)量大于最大數(shù)(65535,2的16次方-1)就拋出一個(gè)Error。
    • 如果當(dāng)且寫線程數(shù)為0(那么讀線程也應(yīng)該為0,因?yàn)樯厦嬉呀?jīng)處理c!=0的情況),并且當(dāng)前線程需要阻塞那么就返回失敗;如果通過CAS增加寫線程數(shù)失敗也返回失敗。
    • 如果c=0,w=0或者c>0,w>0(重入),則設(shè)置當(dāng)前線程或鎖的擁有者,返回成功!

    tryAcquire()除了重入條件(當(dāng)前線程為獲取了寫鎖的線程)之外,增加了一個(gè)讀鎖是否存在的判斷。如果存在讀鎖,則寫鎖不能被獲取,原因在于:必須確保寫鎖的操作對(duì)讀鎖可見,如果允許讀鎖在已被獲取的情況下對(duì)寫鎖的獲取,那么正在運(yùn)行的其他讀線程就無法感知到當(dāng)前寫線程的操作。

    因此,只有等待其他讀線程都釋放了讀鎖,寫鎖才能被當(dāng)前線程獲取,而寫鎖一旦被獲取,則其他讀寫線程的后續(xù)訪問均被阻塞。寫鎖的釋放與ReentrantLock的釋放過程基本類似,每次釋放均減少寫狀態(tài),當(dāng)寫狀態(tài)為0時(shí)表示寫鎖已被釋放,然后等待的讀寫線程才能夠繼續(xù)訪問讀寫鎖,同時(shí)前次寫線程的修改對(duì)后續(xù)的讀寫線程可見。

    接著是讀鎖的代碼:

    可以看到在tryAcquireShared(int unused)方法中,如果其他線程已經(jīng)獲取了寫鎖,則當(dāng)前線程獲取讀鎖失敗,進(jìn)入等待狀態(tài)。如果當(dāng)前線程獲取了寫鎖或者寫鎖未被獲取,則當(dāng)前線程(線程安全,依靠CAS保證)增加讀狀態(tài),成功獲取讀鎖。讀鎖的每次釋放(線程安全的,可能有多個(gè)讀線程同時(shí)釋放讀鎖)均減少讀狀態(tài),減少的值是“1<<16”。所以讀寫鎖才能實(shí)現(xiàn)讀讀的過程共享,而讀寫、寫讀、寫寫的過程互斥。

    此時(shí),我們?cè)倩仡^看一下互斥鎖ReentrantLock中公平鎖和非公平鎖的加鎖源碼:

    我們發(fā)現(xiàn)在ReentrantLock雖然有公平鎖和非公平鎖兩種,但是它們添加的都是獨(dú)享鎖。根據(jù)源碼所示,當(dāng)某一個(gè)線程調(diào)用lock方法獲取鎖時(shí),如果同步資源沒有被其他線程鎖住,那么當(dāng)前線程在使用CAS更新state成功后就會(huì)成功搶占該資源。而如果公共資源被占用且不是被當(dāng)前線程占用,那么就會(huì)加鎖失敗。所以可以確定ReentrantLock無論讀操作還是寫操作,添加的鎖都是都是獨(dú)享鎖。

    1.8. Java中Volatile關(guān)鍵字(重要)

    基本概念:Java 內(nèi)存模型中的可見性、原子性有序性。

    原子性:(原子是世界上的最小單位,具有不可分割性)原子性就是指該操作是不可再分的。不論是多核還是單核,具有原子性的量,同一時(shí)刻只能有一個(gè)線程來對(duì)它進(jìn)行操作。簡(jiǎn)而言之,在整個(gè)操作過程中不會(huì)被線程調(diào)度器中斷的操作,都可認(rèn)為是原子性。比如 a = 1;

    非原子性:

    也就是整個(gè)過程中會(huì)出現(xiàn)線程調(diào)度器中斷操作的現(xiàn)象

    類似"a ++"這樣的操作不具有原子性,因?yàn)樗赡芤?jīng)過以下兩個(gè)步驟:

    (1)取出 a 的值

    (2)計(jì)算 a+1

    如果有兩個(gè)線程t1,t2都在進(jìn)行這樣的操作。t1在第一步做完之后還沒來得及加1操作就被線程調(diào)度器中斷了,于是t2開始執(zhí)行,t2執(zhí)行完畢后t1開始執(zhí)行第二步(此時(shí)t1中a的值可能還是舊值,不是一定的,只有線程t2中a的值沒有及時(shí)更新到t1中才會(huì)出現(xiàn))。這個(gè)時(shí)候就出現(xiàn)了錯(cuò)誤,t2的操作相當(dāng)于被忽略了

    類似于a += 1這樣的操作都不具有原子性。還有一種特殊情況,就是long跟double類型某些情況也不具有原子性

    只有簡(jiǎn)單的讀取、賦值(而且必須是將數(shù)字賦值給某個(gè)變量,變量之間的相互賦值不是原子操作)才是原子操作。

    舉例:請(qǐng)分析以下哪些操作是原子性操作:

    x = 10; //語句1 y = x; //語句2 x++; //語句3 x = x + 1; //語句4

    其實(shí)只有語句1是原子性操作,其他三個(gè)語句都不是原子性操作。

    語句1是直接將數(shù)值10賦值給x,也就是說線程執(zhí)行這個(gè)語句的會(huì)直接將數(shù)值10寫入到工作內(nèi)存中。

    語句2實(shí)際上包含2個(gè)操作,它先要去讀取x的值,再將x的值寫入工作內(nèi)存,雖然讀取x的值以及將x的值寫入工作內(nèi)存 這2個(gè)操作都是原子性操作,但是合起來就不是原子性操作了。

    同樣的,x++和 x = x+1包括2個(gè)操作:讀取x的值,進(jìn)行加1操作,寫入新的值。

    如何保證原子性?

    synchronized、Lock、cas原子類工具

    由于synchronized和Lock能夠保證任一時(shí)刻只有一個(gè)線程執(zhí)行該代碼塊,那么自然就不存在原子性問題了,從而保證了原子性。其次cas原子類工具。

    共享變量:如果一個(gè)變量在多個(gè)線程的工作內(nèi)存中都存在副本,那么這個(gè)變量就是這個(gè)幾個(gè)線程的共享變量

    可見性:一個(gè)線程對(duì)共享變量值的修改,能夠及時(shí)的被其它線程看到。也就是一個(gè)線程對(duì)共享變量修改的結(jié)果,另一個(gè)線程馬上就能看到修改的值。

    如何保證可見性?

    volatile、synchronized、Lock

    要想實(shí)現(xiàn)變量的一定可見,可以使用volatile。另外,通過synchronized和Lock也能夠保證可見性,synchronized和Lock能保證同一時(shí)刻只有一個(gè)線程獲取鎖然后執(zhí)行同步代碼,并且在釋放鎖之前會(huì)將對(duì)變量的修改刷新到主存當(dāng)中。因此可以保證可見性。(其實(shí)還有final,但是它初始化后,值不可更改,所以一般不用它實(shí)現(xiàn)可見性)。

    指令重排:CPU在執(zhí)行代碼時(shí),其實(shí)并不一定會(huì)嚴(yán)格按照我們編寫的順序去執(zhí)行,而是可能會(huì)考慮一些效率方面的原因,對(duì)那些先后順序無關(guān)緊要的代碼進(jìn)行重新排序,這個(gè)操作就被稱為指令重排。指令重排在單線程情況下沒有什么影響,但是在多線程就不一定了。

    有序性:程序執(zhí)行的順序按照代碼先后的順序執(zhí)行。

    如何保證有序性?

    volatile、synchronized、Lock

    volatile:

    volatile原理:Java語言提供了一種稍弱的同步機(jī)制,即volatile變量,用來確保將變量的更新操作通知到其他線程。當(dāng)把變量聲明為volatile類型后,編譯器與運(yùn)行時(shí)都會(huì)注意到這個(gè)變量是共享的,因此不會(huì)將該變量上的操作與其他內(nèi)存操作一起重排序。volatile變量不會(huì)被緩存在寄存器或者對(duì)其他處理器不可見的地方,因此在讀取volatile類型的變量時(shí)總會(huì)返回最新寫入的值。

    在訪問volatile變量時(shí)不會(huì)執(zhí)行加鎖操作,因此也就不會(huì)使執(zhí)行線程阻塞,因此volatile變量是一種比sychronized關(guān)鍵字更輕量級(jí)的同步機(jī)制。

    當(dāng)對(duì)非 volatile 變量進(jìn)行讀寫的時(shí)候,每個(gè)線程先從內(nèi)存拷貝變量到CPU緩存中。如果計(jì)算機(jī)有多個(gè)CPU,每個(gè)線程可能在不同的CPU上被處理,這意味著每個(gè)線程可以拷貝到不同的CPU cache 中。

    而聲明變量是 volatile 的,JVM 保證了每次讀變量都從內(nèi)存中讀,跳過 CPU cache 這一步。

    當(dāng)一個(gè)變量定義為 volatile 之后,將具備兩種特性:

    1.保證此變量對(duì)所有的線程的可見性。當(dāng)一個(gè)線程修改了這個(gè)變量的值,volatile 保證了新值能立即同步到主內(nèi)存,以及每次使用前立即從主內(nèi)存刷新。而普通的共享變量不能保證可見性,因?yàn)槠胀ü蚕碜兞勘恍薷闹?#xff0c;什么時(shí)候被寫入主內(nèi)存是不確定的,當(dāng)其他線程去讀取時(shí),此時(shí)主內(nèi)存中可能還是原來的舊值,因此無法保證可見性。

    2.禁止指令重排序優(yōu)化。有volatile修飾的變量,賦值后多執(zhí)行了一個(gè)“l(fā)oad addl $0x0, (%esp)”操作,這個(gè)操作相當(dāng)于一個(gè)內(nèi)存屏障(指令重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置),只有一個(gè)CPU訪問內(nèi)存時(shí),并不需要內(nèi)存屏障;(什么是指令重排序:是指CPU采用了允許將多條指令不按程序規(guī)定的順序分開發(fā)送給各相應(yīng)電路單元處理)。

    volatile 性能:
      volatile 的讀性能消耗與普通變量幾乎相同,但是寫操作稍慢,因?yàn)樗枰诒镜卮a中插入許多內(nèi)存屏障指令來保證處理器不發(fā)生亂序執(zhí)行。
      
    volatile為什么不能保證原子性?
    簡(jiǎn)單的說,修改volatile變量分為四步:

    1)讀取volatile變量到local 2)修改變量值 3)local值寫回 4)插入內(nèi)存屏障,即lock指令,讓其他線程可見

    這樣就很容易看出來,前三步都是不安全的,取值和寫回之間,不能保證沒有其他線程修改。原子性需要鎖來保證。(或者可以理解為線程安全需要鎖來保證)。這也就是為什么,volatile只用來保證變量可見性和有序性,但不保證原子性。

    1.9.synchronized同步原理

    數(shù)據(jù)同步需要依賴鎖,那鎖的同步又依賴誰?synchronized給出的答案是在軟件層面依賴JVM,而j.u.c.Lock給出的答案是在硬件層面依賴特殊的CPU指令。

    package com.paddx.test.concurrent; public class SynchronizedDemo {public void method() {synchronized (this) {System.out.println("Method 1 start");}} }

    反編譯結(jié)果:

    2 JVM(java虛擬機(jī))

    2.1. 運(yùn)行時(shí)數(shù)據(jù)區(qū)域

  • Jvm內(nèi)存區(qū)域(運(yùn)行時(shí)數(shù)據(jù)區(qū))劃分:

    程序計(jì)數(shù)器:當(dāng)前線程的字節(jié)碼執(zhí)行位置的指示器。內(nèi)存空間小,線程私有。如果線程正在執(zhí)行一個(gè) Java 方法,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是 Native 方法,這個(gè)計(jì)數(shù)器的值則為 (Undefined)。此內(nèi)存區(qū)域是唯一 一個(gè)在 Java 虛擬機(jī)規(guī)范中沒有規(guī)定任何 OutOfMemoryError 情況的區(qū)域。

    Java虛擬機(jī)棧:線程私有,生命周期和線程一致。描述的Java方法執(zhí)行的內(nèi)存模型,每個(gè)方法在執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame),存儲(chǔ)著局部變量、操作數(shù)棧、動(dòng)態(tài)鏈接和方法出口等。每一個(gè)方法從調(diào)用直至執(zhí)行結(jié)束,就對(duì)應(yīng)著一個(gè)棧幀從虛擬機(jī)棧中入棧到出棧的過程。

    局部變量表:存放了編譯期可知的各種基本類型(boolean、byte、char、short、int、float、long、double)、對(duì)象引用(reference 類型)和 returnAddress 類型(指向了一條字節(jié)碼指令的地址)

    StackOverflowError:線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度。
    OutOfMemoryError:如果虛擬機(jī)??梢詣?dòng)態(tài)擴(kuò)展,而擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存。

    本地方法棧:區(qū)別于 Java 虛擬機(jī)棧的是,Java 虛擬機(jī)棧為虛擬機(jī)執(zhí)行 Java 方法(也就是字節(jié)碼)服務(wù),而本地方法棧為虛擬機(jī)使用到的Native方法服務(wù)。也會(huì)有StackOverflowError和 OutOfMemoryError 異常。

    Java堆:所有對(duì)象實(shí)例分配的區(qū)域。對(duì)于絕大多數(shù)應(yīng)用來說,這塊區(qū)域是 JVM 所管理的內(nèi)存中最大的一塊。線程共享,主要是存放對(duì)象實(shí)例和數(shù)組。內(nèi)部會(huì)劃分出多個(gè)線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer, TLAB)??梢晕挥谖锢砩喜贿B續(xù)的空間,但是邏輯上要連續(xù)。

    OutOfMemoryError:如果堆中沒有內(nèi)存完成實(shí)例分配,并且堆也無法再擴(kuò)展時(shí),拋出該異常。

    方法區(qū):所有已經(jīng)被虛擬機(jī)加載的類的信息、常量、靜態(tài)變量和即時(shí)編輯器編譯后的代碼數(shù)據(jù)

  • 詳細(xì)說明:

    程序計(jì)數(shù)器
    程序計(jì)數(shù)器(Program Counter Register) 是一塊較小的內(nèi)存空間,它可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器。

    字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復(fù)等基礎(chǔ)功能都需要依賴這個(gè)計(jì)數(shù)器來完成。

    由于 Java 虛擬機(jī)的多線程是通過線程輪流切換并分配處理器執(zhí)行時(shí)間的方式來實(shí)現(xiàn)的,在任何一個(gè)確定的時(shí)刻,一個(gè)處理器(對(duì)于多核處理器來說是一個(gè)內(nèi)核)都只會(huì)執(zhí)行一條線程中的指令。

    因此,為了線程切換后能恢復(fù)到正確的執(zhí)行位置,每條線程都需要有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各條線程之間計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ),我們稱這類內(nèi)存區(qū)域?yàn)椤熬€程私有”的內(nèi)存。

    如果線程正在執(zhí)行的是一個(gè) Java 方法,這個(gè)計(jì)數(shù)器記錄的是正在執(zhí)行的虛擬機(jī)字節(jié)碼指令的地址。
    如果線程正在執(zhí)行的是一個(gè) Native 方法,這個(gè)計(jì)數(shù)器值則為空(Undefined)。
    此內(nèi)存區(qū)域是唯一一個(gè)在Java虛擬機(jī)規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

    Java 虛擬機(jī)棧
    Java 虛擬機(jī)棧(Java Virtual Machine Stacks)也是線程私有的,它的生命周期與線程相同。虛擬機(jī)棧描述的是 Java 方法執(zhí)行的內(nèi)存模型,每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame) 用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等消息。每一個(gè)方法從調(diào)用直至執(zhí)行完成的過程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過程。

    局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對(duì)象引用(reference類型,它不等同于對(duì)象本身,可能是一個(gè)指向?qū)ο笃鹗嫉刂返囊弥羔?#xff0c;也可能是指向一個(gè)代表對(duì)象的句柄或其他與此對(duì)象相關(guān)的位置)和 returnAddress 類型(指向了一條字節(jié)碼指令的地址)。

    其中 64 位長(zhǎng)度的 long 和 double 類型的數(shù)據(jù)會(huì)占用兩個(gè)局部變量空間(Slot),其余的數(shù)據(jù)類型只占用一個(gè)。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí),這個(gè)方法需要在幀中分配多大的局部變量空間是完全確定的,在方法運(yùn)行期間不會(huì)改變局部變量表的大小。

    在 Java 虛擬機(jī)規(guī)范中,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常狀態(tài):

    如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的的深度,將拋出 StackOverflowError 異常。
    如果虛擬機(jī)??梢詣?dòng)態(tài)擴(kuò)展(當(dāng)前大部分的Java虛擬機(jī)都可動(dòng)態(tài)擴(kuò)展,只不過Java虛擬機(jī)規(guī)范中也允許固定長(zhǎng)度的虛擬機(jī)棧),如果擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存,就會(huì)拋出 OutOfMemoryError 異常。

    本地方法棧
    本地方法棧(Native Method Stack) 與虛擬機(jī)棧所發(fā)揮的作用是非常相似的,它們之間的區(qū)別不過是虛擬機(jī)棧為虛擬機(jī)執(zhí)行Java方法(也就是字節(jié)碼)服務(wù),而本地方法棧則為虛擬機(jī)使用到的Native方法服務(wù)。

    在虛擬機(jī)規(guī)范中對(duì)本地方法棧中方法使用的語言、使用方式與數(shù)據(jù)結(jié)構(gòu)并沒有強(qiáng)制規(guī)定,因此具體的虛擬機(jī)可以自由實(shí)現(xiàn)它。甚至有的虛擬機(jī)(例如:Sun HotSpot虛擬機(jī))直接就把虛擬機(jī)棧和本地方法棧合二為一。與虛擬機(jī)棧一樣,本地方法棧區(qū)域也會(huì)拋出 StackOverflowError 和 OutOfMemoryError 異常。

    Java 堆
    對(duì)于大多數(shù)應(yīng)用來說,Java 堆(Java Heap) 是 Java 虛擬機(jī)所管理的的內(nèi)存中最大的一塊。Java 堆是被所有線程共享的一塊內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建。此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例都在這里分配內(nèi)存。

    Java堆是垃圾收集器管理的主要區(qū)域,從內(nèi)存回收的角度來看,由于現(xiàn)在收集器基本采用分代收集算法,所以Java堆中還可以細(xì)分為:新生代和老年代;再細(xì)致一點(diǎn)的有 Eden 空間、From Survivor 空間、To Survivor 空間等。

    從內(nèi)存分配的角度來看,線程共享的Java堆中可能劃分出多個(gè)線程私有的分配緩沖區(qū)(Thread Local Allocation Buffer,TLAB)。不過無論如何劃分,都與存放內(nèi)容無關(guān),無論哪個(gè)區(qū)域,存儲(chǔ)的仍然是對(duì)象實(shí)例,進(jìn)一步劃分的目的是為了更好地回收內(nèi)存,或者更快地分配內(nèi)存。

    方法區(qū)
    方法區(qū)(Method Area)與 Java 堆一樣,是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。

    運(yùn)行時(shí)常量池(Runtime Constant Pool) 是方法區(qū)的一部分。Class 文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池(Constant Pool Table),用于存放編譯器生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。

    既然運(yùn)行時(shí)常量池是方法區(qū)的一部分,自然受到方法區(qū)內(nèi)存的限制,當(dāng)常量池?zé)o法再申請(qǐng)到內(nèi)存時(shí)就會(huì)拋出 OutOfMemoryError 異常。

    擴(kuò)展String s1 = "abc"和String s2 = new String(“abc”)的區(qū)別,生成對(duì)象的情況

    指向方法區(qū):"abc"是常量,所以它會(huì)在方法區(qū)中分配內(nèi)存,如果方法區(qū)已經(jīng)給"abc"分配過內(nèi)存,則s1會(huì)直接指向這塊內(nèi)存區(qū)域。

    指向Java堆:new String(“abc”)是重新生成了一個(gè)Java實(shí)例,它會(huì)在Java堆中分配一塊內(nèi)存。

    2.1. GC機(jī)制(重要)

    GC 是 garbage collection 的縮寫, 垃圾回收的意思. 也可以是 Garbage Collector, 也就是垃圾回收器.
    Java的內(nèi)存分配與回收全部由JVM垃圾回收進(jìn)程自動(dòng)完成。

    面試題:“你能不能談?wù)?#xff0c;java GC”

    1、哪些對(duì)象可以被回收。 2、何時(shí)回收這些對(duì)象。 3、采用什么樣的方式回收。

    問題1:哪些對(duì)象可以被回收?

    對(duì)象存活判斷(如何判斷對(duì)象可回收/垃圾搜集)
    判斷一個(gè)對(duì)象可以回收通常采用的算法是引用計(jì)數(shù)算法可達(dá)性分析算法。由于互相引用導(dǎo)致的計(jì)數(shù)不好判斷,Java采用的可達(dá)性算法。

  • 引用計(jì)數(shù)算法
    每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可以回收。此方法簡(jiǎn)單,效率很高,但是主流的JVM并沒有選用這種算法來判定可回收對(duì)象,因?yàn)樗幸粋€(gè)致命的缺陷,那就是它無法解決對(duì)象之間相互循環(huán)引用的的問題對(duì)于循環(huán)引用的對(duì)象它無法進(jìn)行回收。例:

    public class Object {public Object instance;public static void main(String[] args) {// 1Object objectA = new Object();Object objectB = new Object();// 2objectA.instance = objectB;objectB.instance = objectA;// 3objectA = null;objectB = null;}

    程序啟動(dòng)后,objectA和objectB兩個(gè)對(duì)象被創(chuàng)建并在堆中分配內(nèi)存,這兩個(gè)對(duì)象都相互持有對(duì)方的引用,除此之外,這兩個(gè)對(duì)象再無任何其他引用,實(shí)際上這兩個(gè)對(duì)象已經(jīng)不可能再被訪問(引用被置空,無法訪問),但是它們因?yàn)橄嗷ヒ弥鴮?duì)方,導(dǎo)致它們的引用計(jì)數(shù)器都不為0,于是引用計(jì)數(shù)算法無法通知GC收集器回收它們。

    實(shí)際上,當(dāng)?shù)?步執(zhí)行時(shí),兩個(gè)對(duì)象的引用計(jì)數(shù)器值都為1;當(dāng)?shù)?步執(zhí)行時(shí),兩個(gè)對(duì)象的引用計(jì)數(shù)器都為2;當(dāng)?shù)?步執(zhí)行時(shí),二者都清為空值,引用計(jì)數(shù)器值都變?yōu)?。根據(jù)引用計(jì)數(shù)算法的思想,值不為0的對(duì)象被認(rèn)為是存活的,不會(huì)被回收;而事實(shí)上這兩個(gè)對(duì)象已經(jīng)不可能再被訪問了,應(yīng)該被回收。

  • 可達(dá)性分析算法(根搜索算法)
    在主流的JVM實(shí)現(xiàn)中,都是通過可達(dá)性分析算法來判定對(duì)象是否存活的??蛇_(dá)性分析算法的基本思想是:通過一系列被稱為"GC Roots"的對(duì)象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索走過的路徑稱為引用鏈,當(dāng)一個(gè)對(duì)象到GC Roots對(duì)象沒有任何引用鏈相連,就認(rèn)為GC Roots到這個(gè)對(duì)象是不可達(dá)的,判定此對(duì)象為不可用對(duì)象,可以被回收。

    在上圖中,objectA、objectB、objectC是可達(dá)的,不會(huì)被回收;objectD、objectE雖然有關(guān)聯(lián),但是它們到GC Roots是不可達(dá)的,所以它們將會(huì)被判定為是可回收的對(duì)象。

  • 在Java中,可作為GC Roots的對(duì)象包括下面幾種:

  • 虛擬機(jī)棧中引用的對(duì)象
    虛擬機(jī)棧中的引用的對(duì)象可以作為GC Root。我們程序在虛擬機(jī)的棧中執(zhí)行,每次函數(shù)調(diào)用調(diào)用都是一次入棧。在棧中包括局部變量表和操作數(shù)棧,局部變量表中的變量可能為引用類型(reference),他們引用的對(duì)象即可作為GC Root。不過隨著函數(shù)調(diào)用結(jié)束出棧,這些引用便會(huì)消失。

  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
    簡(jiǎn)單的說就是我們?cè)陬愔惺褂玫膕tatic聲明的引用類型字段,例如:

    Class Dog {private static Object tail; }
  • 方法區(qū)中常量引用的對(duì)象
    簡(jiǎn)單的說就是我們?cè)陬愔惺褂胒inal聲明的引用類型字段,例如:

    Class Dog {private final Object tail; }
  • 本地方法棧中引用的對(duì)象
    就是程序中native本地方法引用的對(duì)象。

  • 問題3:采用什么樣的方式回收

    GC常用算法

    可達(dá)性分析算法只是知道了哪些對(duì)象可以回收,不過垃圾收集顯然還需要解決后兩個(gè)問題,什么時(shí)候回收以及如何回收,在根搜索算法的基礎(chǔ)上,現(xiàn)代虛擬機(jī)的實(shí)現(xiàn)當(dāng)中,垃圾搜集的算法主要有三種,分別是標(biāo)記-清除算法、復(fù)制算法、標(biāo)記-整理算法,這三種算法都擴(kuò)充了根搜索算法,不過它們理解起來還是非常好理解的。

    標(biāo)記 -清除算法

    就是當(dāng)程序運(yùn)行期間,若可以使用的內(nèi)存被耗盡的時(shí)候,GC線程就會(huì)被觸發(fā)并將程序暫停,隨后將依舊存活的對(duì)象標(biāo)記一遍,最終再將堆中所有沒被標(biāo)記的對(duì)象全部清除掉,接下來便讓程序恢復(fù)運(yùn)行。之所以說它是最基礎(chǔ)的收集算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并對(duì)其缺點(diǎn)進(jìn)行改進(jìn)而得到的。

    它的主要缺點(diǎn)有兩個(gè):一個(gè)是效率問題,標(biāo)記和清除過程的效率都不高(遞歸與全堆對(duì)象遍歷);另外一個(gè)是空間問題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,內(nèi)存的布局自然會(huì)亂七八糟。空間碎片太多可能會(huì)導(dǎo)致,當(dāng)程序在以后的運(yùn)行過程中需要分配較大對(duì)象時(shí)無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。

    復(fù)制算法
    “復(fù)制”(Copying)的收集算法,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。

    這樣使得每次都是對(duì)其中的一塊進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。只是這種算法的代價(jià)是將內(nèi)存縮小為原來的一半,持續(xù)復(fù)制長(zhǎng)生存期的對(duì)象則導(dǎo)致效率降低。

    標(biāo)記-整理算法
    復(fù)制收集算法在對(duì)象存活率較高時(shí)就要執(zhí)行較多的復(fù)制操作,效率將會(huì)變低。更關(guān)鍵的是,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都100%存活的極端情況,所以在老年代一般不能直接選用這種算法。

    根據(jù)老年代的特點(diǎn),有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存。標(biāo)記/整理算法唯一的缺點(diǎn)就是效率也不高,不僅要標(biāo)記所有存活對(duì)象,還要整理所有存活對(duì)象的引用地址。從效率上來說,標(biāo)記/整理算法要低于復(fù)制算法。

    分代搜集算法(重要)
    GC 分代的基本假設(shè):絕大部分對(duì)象的生命周期都非常短暫,存活時(shí)間短。

    “分代搜集”算法,把Java堆分為新生代老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴?。在新生代?#xff0c;每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。而老年代中因?yàn)閷?duì)象存活率高、沒有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清理”或“標(biāo)記-整理”算法來進(jìn)行回收。

    新生代GC(minor GC):指發(fā)生在新生代的垃圾回收動(dòng)作,因?yàn)镴ava對(duì)象大多都具備朝生夕滅的特點(diǎn),所以minor GC發(fā)生得非常頻繁,一般回收速度也比較塊。老年代GC(Major GC/Full GC):指發(fā)生在老年代的GC,它的速度會(huì)比minor GC慢很多。

    問題2:何時(shí)回收這些對(duì)象

    回收的時(shí)機(jī)
    JVM在進(jìn)行GC時(shí),并非每次都對(duì)上面三個(gè)內(nèi)存區(qū)域一起回收的,大部分時(shí)候回收的都是指新生代。因此GC按照回收的區(qū)域又分了兩種類型,一種是普通GC(minor GC),一種是全局GC(major GC or Full GC),它們所針對(duì)的區(qū)域如下。普通GC(minor GC):只針對(duì)新生代區(qū)域的GC。全局GC(major GC or Full GC):針對(duì)年老代的GC,偶爾伴隨對(duì)新生代的GC以及對(duì)永久代的GC。由于年老代與永久代相對(duì)來說GC效果不好,而且二者的內(nèi)存使用增長(zhǎng)速度也慢,因此一般情況下,需要經(jīng)過好幾次普通GC,才會(huì)觸發(fā)一次全局GC。

    內(nèi)存模型與回收策略

    Java 堆(Java Heap)是JVM所管理的內(nèi)存中最大的一塊,堆又是垃圾收集器管理的主要區(qū)域,Java 堆主要分為2個(gè)區(qū)域-新生代與老年代,其中年輕代又分 Eden 區(qū)和 Survivor 區(qū),其中 Survivor 區(qū)又分 From 和 To 2個(gè)區(qū)。

    Eden 區(qū)
    大多數(shù)情況下,對(duì)象會(huì)在新生代 Eden 區(qū)中進(jìn)行分配,當(dāng) Eden 區(qū)沒有足夠空間進(jìn)行分配時(shí),虛擬機(jī)會(huì)發(fā)起一次 Minor GC,Minor GC 相比 Major GC 更頻繁,回收速度也更快。 通過 Minor GC 之后,Eden 會(huì)被清空,Eden 區(qū)中絕大部分對(duì)象會(huì)被回收,而那些無需回收的存活對(duì)象,將會(huì)進(jìn)到 Survivor 的 From 區(qū)(若 From 區(qū)不夠,則直接進(jìn)入 Old 區(qū))。

    Survivor 區(qū)
    Survivor 區(qū)相當(dāng)于是 Eden 區(qū)和 Old 區(qū)的一個(gè)緩沖,類似于我們交通燈中的黃燈。Survivor 又分為2個(gè)區(qū),一個(gè)是 From 區(qū),一個(gè)是 To 區(qū)。每次執(zhí)行 Minor GC,會(huì)將 Eden 區(qū)和 From 存活的對(duì)象放到 Survivor 的 To 區(qū)(如果 To 區(qū)不夠,則直接進(jìn)入 Old 區(qū))。Survivor 的存在意義就是減少被送到老年代的對(duì)象,進(jìn)而減少 Major GC 的發(fā)生。Survivor 的預(yù)篩選保證,只有經(jīng)歷16次 Minor GC 還能在新生代中存活的對(duì)象,才會(huì)被送到老年代。

    Old 區(qū)
    老年代占據(jù)著2/3的堆內(nèi)存空間,只有在 Major GC 的時(shí)候才會(huì)進(jìn)行清理,每次 GC 都會(huì)觸發(fā)“Stop-The-World”。內(nèi)存越大,STW 的時(shí)間也越長(zhǎng),所以內(nèi)存也不僅僅是越大就越好。由于復(fù)制算法在對(duì)象存活率較高的老年代會(huì)進(jìn)行很多次的復(fù)制操作,效率很低,所以老年代這里采用的是標(biāo)記——整理算法。

    java垃圾收集器:(共7種,著重了解CMS和G1)

    CMS收集器

    CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。目前很大一部分的 Java 應(yīng)用都集中在互聯(lián)網(wǎng)站或B/S系統(tǒng)的服務(wù)端上,這類應(yīng)用尤其重視服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短,以給用戶帶來較好的體驗(yàn)。

    從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,它的運(yùn)作過程相對(duì)于前面幾種收集器來說要更復(fù)雜一些,整個(gè)過程分為4個(gè)步驟,包括:

    初始標(biāo)記(CMS initial mark)
    并發(fā)標(biāo)記(CMS concurrent mark)
    重新標(biāo)記(CMS remark)
    并發(fā)清除(CMS concurrent sweep)
    其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要“Stop The World”。初始標(biāo)記僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度很快,并發(fā)標(biāo)記階段就是進(jìn)行GC Roots Tracing的過程,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段稍長(zhǎng)一些,但遠(yuǎn)比并發(fā)標(biāo)記的時(shí)間短。

    由于整個(gè)過程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過程中,收集器線程都可以與用戶線程一起工作,所以總體上來說,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)地執(zhí)行。老年代收集器(新生代使用ParNew)

    G1收集器

    與CMS收集器相比G1收集器有以下特點(diǎn):

    1、空間整合,G1收集器采用標(biāo)記整理算法,不會(huì)產(chǎn)生內(nèi)存空間碎片。分配大對(duì)象時(shí)不會(huì)因?yàn)闊o法找到連續(xù)空間而提前觸發(fā)下一次GC。

    2、可預(yù)測(cè)停頓,這是G1的另一大優(yōu)勢(shì),降低停頓時(shí)間是G1和CMS的共同關(guān)注點(diǎn),但G1除了追求低停頓外,還能建立可預(yù)測(cè)的停頓時(shí)間模型,能讓使用者明確指定在一個(gè)長(zhǎng)度為N毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不得超過N毫秒,這幾乎已經(jīng)是實(shí)時(shí) Java(RTSJ)的垃圾收集器的特征了。

    使用G1收集器時(shí),Java堆的內(nèi)存布局與其他收集器有很大差別,它將整個(gè)Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分(可以不連續(xù))Region 的集合。

    G1的新生代收集跟 ParNew 類似,當(dāng)新生代占用達(dá)到一定比例的時(shí)候,開始出發(fā)收集。和 CMS 類似,G1 收集器收集老年代對(duì)象會(huì)有短暫停頓。

    2.3. 類加載過程

    類加載的時(shí)機(jī):

    • 隱式加載:new創(chuàng)建類的實(shí)例
    • 顯式加載:loaderClass,forName等
    • 操作類的靜態(tài)變量(使用或賦值)
    • 調(diào)用類的靜態(tài)方法
    • 使用反射方式創(chuàng)建某個(gè)類或者接口對(duì)象的Class對(duì)象。
    • 初始化某個(gè)類的子類
    • 直接使用java.exe命令來運(yùn)行某個(gè)主類

    類的加載過程:

    其中加載、驗(yàn)證、準(zhǔn)備、初始化和卸載這五個(gè)階段的順序是確定的。解析階段可以在初始化之后再開始(運(yùn)行時(shí)綁定或動(dòng)態(tài)綁定或晚期綁定)。

  • 加載:ClassLoader通過一個(gè)類的完全限定名查找此類字節(jié)碼文件,并利用字節(jié)碼文件創(chuàng)建一個(gè)class對(duì)象。

  • 驗(yàn)證:對(duì)類的驗(yàn)證。目的在于確保class文件的字節(jié)流中包含信息符合當(dāng)前虛擬機(jī)要求,不會(huì)危害虛擬機(jī)自身的安全,主要包括四種驗(yàn)證:文件格式的驗(yàn)證,元數(shù)據(jù)的驗(yàn)證,字節(jié)碼驗(yàn)證,符號(hào)引用驗(yàn)證。

  • 準(zhǔn)備為類變量分配內(nèi)存并設(shè)置初始值。
    類變量(static修飾的字段變量)分配內(nèi)存并且設(shè)置該類變量的初始值,(如static int i = 5 這里只是將 i 賦值為0,在初始化的階段再把 i 賦值為5),這里不包含final修飾的static ,因?yàn)閒inal在編譯的時(shí)候就已經(jīng)分配了。這里不會(huì)為實(shí)例變量分配初始化,類變量會(huì)分配在方法區(qū)中,實(shí)例變量會(huì)隨著對(duì)象分配到Java堆中。

  • 解析:將常量池的符號(hào)引用轉(zhuǎn)化為直接引用。

  • 初始化:這里是類加載的最后階段,前面過程都是以虛擬機(jī)主導(dǎo),而初始化階段開始執(zhí)行類中定義的Java程序代碼,包括類變量的賦值動(dòng)作和構(gòu)造函數(shù)的賦值。
    如果該類具有父類就進(jìn)行對(duì)父類進(jìn)行初始化,執(zhí)行其靜態(tài)初始化器(靜態(tài)代碼塊)和靜態(tài)初始化成員變量。(前面已經(jīng)對(duì)static 初始化了默認(rèn)值,這里我們對(duì)它進(jìn)行賦值,成員變量也將被初始化)

  • 使用

  • 卸載

  • 只有加載、驗(yàn)證、準(zhǔn)備、初始化和卸載的這個(gè)五個(gè)階段的順序是確定的。

    2.4. 雙親委派模型

    類加載的機(jī)制,以及為什么要這樣設(shè)計(jì)?

    類加載的機(jī)制是雙親委派模型。大部分Java程序需要使用的類加載器包括:

    • 啟動(dòng)類加載器:由C++語言實(shí)現(xiàn),負(fù)責(zé)加載Java中的核心類。
    • 擴(kuò)展類加載器:負(fù)責(zé)加載Java擴(kuò)展的核心類之外的類。
    • 應(yīng)用程序類加載器:負(fù)責(zé)加載用戶類路徑上指定的類庫

    雙親委派模型如下:

    雙親委派模型要求出了頂層的啟動(dòng)類加載器之外,其他的類加載器都有自己的父加載器,通過組合實(shí)現(xiàn)。

    雙親委派模型的工作流程/原理:

    簡(jiǎn)單說:
    當(dāng)一個(gè)類加載的任務(wù)來臨的時(shí)候,先交給父類加載器完成,父類加載器交給父父類加載器完成,知道傳遞給啟動(dòng)類加載器,如果完成不了的情況下,再依次往下傳遞類加載的任務(wù)。

    詳細(xì)解釋:
    如果一個(gè)類收到了類加載的請(qǐng)求,它并不會(huì)自己先去加載,而是把這個(gè)請(qǐng)求委托給父類加載器去執(zhí)行,如果父類加載器還存在父類加載器,則進(jìn)一步向上委托,依次遞歸,請(qǐng)求最后到達(dá)頂層的啟動(dòng)類加載器,如果父類能夠完成類的加載任務(wù),就會(huì)成功返回,倘若父類加載器無法完成任務(wù),子類加載器才會(huì)嘗試自己去加載,這就是雙親委派模式。就是每個(gè)兒子都很懶,遇到類加載的活都給它爸爸干,直到爸爸說我也做不來的時(shí)候,兒子才會(huì)想辦法自己去加載。

    雙親委派模型的優(yōu)勢(shì)?這樣設(shè)計(jì)的原因?:

    簡(jiǎn)單說:
    雙親委派模型能夠保證Java程序的穩(wěn)定運(yùn)行,不同層次的類加載器具有不同優(yōu)先級(jí),所有的對(duì)象的父類Object,無論哪一個(gè)類加載器加載,最后都會(huì)交給啟動(dòng)類加載器,保證安全。

    詳細(xì)解釋:
    采用雙親委派模式的好處就是Java類隨著它的類加載器一起具備一種帶有優(yōu)先級(jí)的層次關(guān)系,通過這種層級(jí)關(guān)系可以避免類的重復(fù)加載,當(dāng)父親已經(jīng)加載了該類的時(shí)候,就沒有必要子類加載器(ClassLoader)再加載一次。其次是考慮到安全因素,Java核心API中定義類型不會(huì)被隨意替換,假設(shè)通過網(wǎng)路傳遞一個(gè)名為java.lang.Integer的類,通過雙親委派的的模式傳遞到啟動(dòng)類加載器,而啟動(dòng)類加載器在核心Java API發(fā)現(xiàn)這個(gè)名字類,發(fā)現(xiàn)該類已經(jīng)被加載,并不會(huì)重新加載網(wǎng)絡(luò)傳遞過來的java.lang.Integer.而之際返回已經(jīng)加載過的Integer.class,這樣便可以防止核心API庫被隨意篡改??赡苣銜?huì)想,如果我們?cè)赾alsspath路徑下自定義一個(gè)名為java.lang.SingInteger該類并不存在java.lang中,經(jīng)過雙親委托模式,傳遞到啟動(dòng)類加載器中,由于父類加載器路徑下并沒有該類,所以不會(huì)加載,將反向委托給子類加載器,最終會(huì)通過系統(tǒng)類加載器加載該類,但是這樣做是不允許的,因?yàn)閖ava.lang是核心的API包,需要訪問權(quán)限,強(qiáng)制加載將會(huì)報(bào)出如下異常。

    java.lang.SecurityException:Prohibited package name: java.lang

    為什么叫雙親委派?
    parents delegate

    parents在英文中是“父母”、“雙親”的意思,但其實(shí)表達(dá)的是“父母這一輩”的人的意思。實(shí)際上這個(gè)模型中,只是表達(dá)“父母這一輩”的class loader而已,并不是說真的有一個(gè)父親的class loader和一個(gè)母親class loader。

    簡(jiǎn)單來說,就是翻譯的人,不僅英語不好,而且也不理解jvm的類加載機(jī)制,才會(huì)導(dǎo)致翻譯成這樣

    先自我介紹一下,小編13年上師交大畢業(yè),曾經(jīng)在小公司待過,去過華為OPPO等大廠,18年進(jìn)入阿里,直到現(xiàn)在。深知大多數(shù)初中級(jí)java工程師,想要升技能,往往是需要自己摸索成長(zhǎng)或是報(bào)班學(xué)習(xí),但對(duì)于培訓(xùn)機(jī)構(gòu)動(dòng)則近萬元的學(xué)費(fèi),著實(shí)壓力不小。自己不成體系的自學(xué)效率很低又漫長(zhǎng),而且容易碰到天花板技術(shù)停止不前。因此我收集了一份《java開發(fā)全套學(xué)習(xí)資料》送給大家,初衷也很簡(jiǎn)單,就是希望幫助到想自學(xué)又不知道該從何學(xué)起的朋友,同時(shí)減輕大家的負(fù)擔(dān)。添加下方名片,即可獲取全套學(xué)習(xí)資料哦

    總結(jié)

    以上是生活随笔為你收集整理的Android复习系列②之《Java进阶》的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    野狼第一精品社区 | 精品国偷自产在线视频 | 国内丰满熟女出轨videos | 天天燥日日燥 | 亚洲爆乳精品无码一区二区三区 | 国产精品免费大片 | 久久久久国色av免费观看性色 | 日韩欧美中文字幕公布 | 色狠狠av一区二区三区 | 性啪啪chinese东北女人 | 四虎4hu永久免费 | 人人超人人超碰超国产 | 国内少妇偷人精品视频 | 欧美日韩一区二区三区自拍 | 精品水蜜桃久久久久久久 | 网友自拍区视频精品 | 亚洲国产欧美日韩精品一区二区三区 | 国产精品无码一区二区三区不卡 | 色噜噜亚洲男人的天堂 | 亚洲中文字幕成人无码 | 亚洲人成网站免费播放 | 黑人巨大精品欧美一区二区 | 中文亚洲成a人片在线观看 | 久久综合激激的五月天 | 夜先锋av资源网站 | 色婷婷久久一区二区三区麻豆 | 两性色午夜视频免费播放 | 日韩av无码一区二区三区不卡 | 亚洲一区av无码专区在线观看 | 国产一区二区三区影院 | 久久精品国产日本波多野结衣 | 亚洲精品久久久久久久久久久 | 亚洲s码欧洲m码国产av | 99在线 | 亚洲 | 99久久99久久免费精品蜜桃 | 亚洲乱码国产乱码精品精 | 欧美性生交活xxxxxdddd | 日日噜噜噜噜夜夜爽亚洲精品 | 四虎影视成人永久免费观看视频 | 久久国产精品精品国产色婷婷 | 东京热无码av男人的天堂 | 亚洲小说图区综合在线 | 亚洲国产精品久久久天堂 | 清纯唯美经典一区二区 | 人妻少妇被猛烈进入中文字幕 | 国产精品福利视频导航 | 日韩欧美群交p片內射中文 | 亚洲中文字幕无码中文字在线 | 全球成人中文在线 | 乌克兰少妇性做爰 | 精品国产精品久久一区免费式 | 亚洲s色大片在线观看 | 99久久精品国产一区二区蜜芽 | 亚洲成av人在线观看网址 | 乱码午夜-极国产极内射 | 亚洲一区二区三区偷拍女厕 | 小泽玛莉亚一区二区视频在线 | 精品久久久久香蕉网 | 亚洲精品成a人在线观看 | 国产办公室秘书无码精品99 | 特黄特色大片免费播放器图片 | 2020久久超碰国产精品最新 | 精品国产福利一区二区 | 一本久久a久久精品vr综合 | 成年美女黄网站色大免费视频 | 日本精品人妻无码77777 天堂一区人妻无码 | 成熟人妻av无码专区 | 亚洲一区二区三区四区 | 性开放的女人aaa片 | 丝袜足控一区二区三区 | 人妻少妇被猛烈进入中文字幕 | 国产av一区二区三区最新精品 | 亚洲色欲色欲欲www在线 | 日本高清一区免费中文视频 | www一区二区www免费 | 少妇无套内谢久久久久 | 男女爱爱好爽视频免费看 | 国产精品无码久久av | 日韩欧美群交p片內射中文 | 国产精品久久国产精品99 | 国产口爆吞精在线视频 | aⅴ亚洲 日韩 色 图网站 播放 | 国产超碰人人爽人人做人人添 | 黑人大群体交免费视频 | 日日噜噜噜噜夜夜爽亚洲精品 | 国产午夜手机精彩视频 | 成熟人妻av无码专区 | 亚洲第一网站男人都懂 | 夜先锋av资源网站 | 婷婷色婷婷开心五月四房播播 | 人妻体内射精一区二区三四 | 精品国产国产综合精品 | 久久综合久久自在自线精品自 | 亚洲精品无码人妻无码 | 男女爱爱好爽视频免费看 | 97夜夜澡人人双人人人喊 | 人人妻人人澡人人爽欧美一区 | 在线观看国产午夜福利片 | 在线视频网站www色 | 国产熟妇高潮叫床视频播放 | 99国产精品白浆在线观看免费 | 亚洲成熟女人毛毛耸耸多 | 丰满人妻被黑人猛烈进入 | 日韩av无码一区二区三区不卡 | 亚洲一区二区三区国产精华液 | 亚洲色偷偷男人的天堂 | 99视频精品全部免费免费观看 | 日韩 欧美 动漫 国产 制服 | 伊人久久大香线焦av综合影院 | 性欧美videos高清精品 | 日韩欧美群交p片內射中文 | 国产精品久久福利网站 | 国产精品嫩草久久久久 | 亚洲va欧美va天堂v国产综合 | 一本色道婷婷久久欧美 | 国产深夜福利视频在线 | 又紧又大又爽精品一区二区 | 亚洲中文字幕无码中文字在线 | 亚洲爆乳精品无码一区二区三区 | 在线 国产 欧美 亚洲 天堂 | 欧洲欧美人成视频在线 | 久久久久亚洲精品中文字幕 | 亚洲 激情 小说 另类 欧美 | 俺去俺来也在线www色官网 | 曰韩少妇内射免费播放 | 漂亮人妻洗澡被公强 日日躁 | 国产在线无码精品电影网 | 日韩人妻少妇一区二区三区 | 国产成人无码av片在线观看不卡 | 啦啦啦www在线观看免费视频 | 日本熟妇乱子伦xxxx | 图片小说视频一区二区 | 国产特级毛片aaaaaaa高清 | 日韩欧美成人免费观看 | 欧美日本精品一区二区三区 | 亚洲精品综合五月久久小说 | 精品日本一区二区三区在线观看 | 欧美人与禽zoz0性伦交 | 国产精品无码一区二区三区不卡 | 国产午夜视频在线观看 | 国产婷婷色一区二区三区在线 | 国产精品va在线播放 | 欧美人与牲动交xxxx | 最新国产乱人伦偷精品免费网站 | 在教室伦流澡到高潮hnp视频 | 女人被爽到呻吟gif动态图视看 | 女人和拘做爰正片视频 | 蜜桃臀无码内射一区二区三区 | 日韩精品无码免费一区二区三区 | 色婷婷综合激情综在线播放 | 爆乳一区二区三区无码 | 国产一区二区不卡老阿姨 | 亚洲精品一区国产 | 亚洲国产欧美日韩精品一区二区三区 | 激情国产av做激情国产爱 | 欧美熟妇另类久久久久久多毛 | 亚洲精品一区二区三区四区五区 | 国产成人无码午夜视频在线观看 | 九月婷婷人人澡人人添人人爽 | 国产黑色丝袜在线播放 | 国产激情综合五月久久 | 国产精品无码久久av | 日产国产精品亚洲系列 | 色诱久久久久综合网ywww | 在线播放无码字幕亚洲 | 人人妻人人澡人人爽人人精品浪潮 | 精品亚洲韩国一区二区三区 | 精品国产成人一区二区三区 | 久久精品国产亚洲精品 | 国产精品久久久久久久9999 | 无码任你躁久久久久久久 | 婷婷五月综合激情中文字幕 | 亚洲无人区一区二区三区 | 国产精品久久久久久亚洲影视内衣 | 奇米影视7777久久精品人人爽 | 国产亚洲精品久久久ai换 | 成人免费视频视频在线观看 免费 | 亚洲日本在线电影 | 国产一区二区三区日韩精品 | 99久久人妻精品免费二区 | 亚洲欧美综合区丁香五月小说 | 久久精品无码一区二区三区 | 18无码粉嫩小泬无套在线观看 | 亚洲综合伊人久久大杳蕉 | 欧美老妇交乱视频在线观看 | 丰满人妻一区二区三区免费视频 | 亚洲性无码av中文字幕 | 久久国产劲爆∧v内射 | 中文字幕精品av一区二区五区 | 色综合久久久久综合一本到桃花网 | 中文亚洲成a人片在线观看 | 国产亚洲欧美日韩亚洲中文色 | 麻豆果冻传媒2021精品传媒一区下载 | 婷婷丁香五月天综合东京热 | 婷婷丁香五月天综合东京热 | 国产欧美精品一区二区三区 | 中文字幕中文有码在线 | 中文字幕无码免费久久99 | 狠狠cao日日穞夜夜穞av | 日本一区二区三区免费播放 | 午夜理论片yy44880影院 | 色五月丁香五月综合五月 | 亚洲乱码中文字幕在线 | 又大又硬又黄的免费视频 | 性做久久久久久久免费看 | 蜜桃无码一区二区三区 | 秋霞成人午夜鲁丝一区二区三区 | 日本肉体xxxx裸交 | 日日干夜夜干 | 精品欧美一区二区三区久久久 | 青青草原综合久久大伊人精品 | 老熟妇乱子伦牲交视频 | 国产深夜福利视频在线 | 一本久道久久综合婷婷五月 | 国产69精品久久久久app下载 | 久久精品成人欧美大片 | 亚洲中文字幕乱码av波多ji | 国产婷婷色一区二区三区在线 | 狠狠色色综合网站 | 51国偷自产一区二区三区 | 中文字幕av无码一区二区三区电影 | 成人试看120秒体验区 | 亚洲无人区午夜福利码高清完整版 | 亚洲精品综合五月久久小说 | 亚洲成a人片在线观看日本 | 色偷偷人人澡人人爽人人模 | 少妇人妻av毛片在线看 | 无码国产激情在线观看 | 国产精品igao视频网 | 亚洲日韩一区二区三区 | 成人亚洲精品久久久久软件 | 天天燥日日燥 | 精品乱子伦一区二区三区 | 久久午夜夜伦鲁鲁片无码免费 | 亚洲小说春色综合另类 | 亚洲高清偷拍一区二区三区 | 蜜桃臀无码内射一区二区三区 | 国产无遮挡又黄又爽又色 | 理论片87福利理论电影 | 中文无码精品a∨在线观看不卡 | 一本久道久久综合婷婷五月 | 日本高清一区免费中文视频 | 少妇性俱乐部纵欲狂欢电影 | 国产精品人妻一区二区三区四 | 精品人妻av区 | 青青青手机频在线观看 | 无套内谢老熟女 | 亚洲欧美日韩综合久久久 | 久久久久国色av免费观看性色 | 久久精品中文闷骚内射 | 一本大道久久东京热无码av | 久久五月精品中文字幕 | aⅴ亚洲 日韩 色 图网站 播放 | 日产国产精品亚洲系列 | 九一九色国产 | 精品一二三区久久aaa片 | 黄网在线观看免费网站 | 久久亚洲中文字幕精品一区 | 国产成人精品一区二区在线小狼 | 成年美女黄网站色大免费视频 | 国产亚洲精品久久久久久大师 | 午夜无码人妻av大片色欲 | 亚洲成av人综合在线观看 | 国产一区二区不卡老阿姨 | 少妇高潮喷潮久久久影院 | 国产精品毛多多水多 | 好屌草这里只有精品 | 秋霞成人午夜鲁丝一区二区三区 | 亚洲国产精品久久人人爱 | 精品偷拍一区二区三区在线看 | 午夜精品一区二区三区在线观看 | 九九热爱视频精品 | 中国女人内谢69xxxxxa片 | 国产无遮挡吃胸膜奶免费看 | 无码av免费一区二区三区试看 | 装睡被陌生人摸出水好爽 | 亚洲理论电影在线观看 | 免费看少妇作爱视频 | 色婷婷综合激情综在线播放 | 香港三级日本三级妇三级 | 成人一区二区免费视频 | 我要看www免费看插插视频 | √8天堂资源地址中文在线 | 大乳丰满人妻中文字幕日本 | 国产农村乱对白刺激视频 | 亚洲日韩av一区二区三区四区 | 成熟妇人a片免费看网站 | 亚洲精品久久久久久一区二区 | 99在线 | 亚洲 | 亚洲人成人无码网www国产 | 国产三级精品三级男人的天堂 | 欧洲熟妇色 欧美 | 国产电影无码午夜在线播放 | 亚洲欧美精品伊人久久 | 亚洲色大成网站www国产 | 樱花草在线播放免费中文 | 97无码免费人妻超级碰碰夜夜 | 亚洲国产精品久久人人爱 | 国产三级精品三级男人的天堂 | 成人毛片一区二区 | 亚洲人成网站在线播放942 | 亚洲中文字幕乱码av波多ji | 天天摸天天透天天添 | 精品乱子伦一区二区三区 | 中文字幕 亚洲精品 第1页 | 黄网在线观看免费网站 | 又粗又大又硬毛片免费看 | 熟妇女人妻丰满少妇中文字幕 | 未满小14洗澡无码视频网站 | 亚洲另类伦春色综合小说 | 综合网日日天干夜夜久久 | 99久久久国产精品无码免费 | 免费无码肉片在线观看 | 少妇一晚三次一区二区三区 | 真人与拘做受免费视频一 | 蜜臀av在线播放 久久综合激激的五月天 | 99久久精品无码一区二区毛片 | 国产成人无码av片在线观看不卡 | 久久久久人妻一区精品色欧美 | 男女爱爱好爽视频免费看 | 九九综合va免费看 | 少妇的肉体aa片免费 | 亚洲精品国偷拍自产在线麻豆 | 岛国片人妻三上悠亚 | 国产精品久久福利网站 | 午夜熟女插插xx免费视频 | 男人的天堂av网站 | 欧美老熟妇乱xxxxx | 熟女少妇在线视频播放 | 欧美成人午夜精品久久久 | 思思久久99热只有频精品66 | 成熟女人特级毛片www免费 | www国产亚洲精品久久网站 | 大乳丰满人妻中文字幕日本 | 亚洲国产精品成人久久蜜臀 | 一本一道久久综合久久 | 亚洲国产欧美日韩精品一区二区三区 | 精品乱子伦一区二区三区 | 亚洲小说春色综合另类 | 狠狠cao日日穞夜夜穞av | 少妇性俱乐部纵欲狂欢电影 | 亚洲成av人片在线观看无码不卡 | 日韩精品久久久肉伦网站 | 女人色极品影院 | 国产又爽又黄又刺激的视频 | 国产精品丝袜黑色高跟鞋 | 少妇性l交大片欧洲热妇乱xxx | 狠狠躁日日躁夜夜躁2020 | 国产日产欧产精品精品app | 久久亚洲国产成人精品性色 | 亚洲欧美中文字幕5发布 | 亚洲娇小与黑人巨大交 | 东京热男人av天堂 | 亚洲综合无码一区二区三区 | 精品国产一区二区三区av 性色 | 久久zyz资源站无码中文动漫 | 亚洲经典千人经典日产 | 女人被爽到呻吟gif动态图视看 | 色偷偷av老熟女 久久精品人妻少妇一区二区三区 | 青青青爽视频在线观看 | 无码av免费一区二区三区试看 | 高清不卡一区二区三区 | 3d动漫精品啪啪一区二区中 | 久久国内精品自在自线 | 国产人成高清在线视频99最全资源 | 欧美精品国产综合久久 | 无码av免费一区二区三区试看 | 国产精品亚洲专区无码不卡 | 国产综合久久久久鬼色 | 福利一区二区三区视频在线观看 | 精品亚洲成av人在线观看 | 天天摸天天透天天添 | 欧美放荡的少妇 | a国产一区二区免费入口 | 欧美日韩色另类综合 | 国产人成高清在线视频99最全资源 | 日韩精品乱码av一区二区 | 亚洲中文字幕在线无码一区二区 | 国产三级精品三级男人的天堂 | 无码人妻丰满熟妇区毛片18 | 色一情一乱一伦一区二区三欧美 | 国产精品国产三级国产专播 | 国产成人综合色在线观看网站 | 久久人人97超碰a片精品 | 欧美乱妇无乱码大黄a片 | 久久精品人人做人人综合 | 亚洲精品国偷拍自产在线观看蜜桃 | 亚洲中文字幕久久无码 | 亚洲熟妇色xxxxx欧美老妇y | 欧美第一黄网免费网站 | 国产亚洲人成在线播放 | 亚洲成av人片在线观看无码不卡 | 国产人妻久久精品二区三区老狼 | 亚洲 另类 在线 欧美 制服 | 小泽玛莉亚一区二区视频在线 | 人妻少妇精品无码专区二区 | 色五月五月丁香亚洲综合网 | 1000部啪啪未满十八勿入下载 | 久久人人爽人人爽人人片av高清 | 日本大香伊一区二区三区 | а天堂中文在线官网 | 久久久久成人片免费观看蜜芽 | 18禁黄网站男男禁片免费观看 | 午夜嘿嘿嘿影院 | 亚洲爆乳无码专区 | 欧美丰满熟妇xxxx | 亚洲国产成人av在线观看 | 欧美成人高清在线播放 | 无码中文字幕色专区 | 日本精品人妻无码77777 天堂一区人妻无码 | 亚洲一区二区三区香蕉 | 日本精品少妇一区二区三区 | 日本护士毛茸茸高潮 | 又大又硬又黄的免费视频 | 亚洲一区二区三区偷拍女厕 | 久久久精品456亚洲影院 | 精品国产精品久久一区免费式 | 中文字幕乱码亚洲无线三区 | 国产欧美亚洲精品a | 97资源共享在线视频 | 51国偷自产一区二区三区 | 欧美freesex黑人又粗又大 | 日本肉体xxxx裸交 | 俺去俺来也www色官网 | 99久久人妻精品免费一区 | 国产三级精品三级男人的天堂 | 精品国精品国产自在久国产87 | 久久www免费人成人片 | 装睡被陌生人摸出水好爽 | 中文字幕 亚洲精品 第1页 | 国产激情一区二区三区 | 国产精品久久国产精品99 | 精品一区二区三区波多野结衣 | 蜜桃无码一区二区三区 | 欧美35页视频在线观看 | 精品久久久久久人妻无码中文字幕 | 日韩人妻无码一区二区三区久久99 | 国产激情综合五月久久 | 国产在线无码精品电影网 | 丰满岳乱妇在线观看中字无码 | 久热国产vs视频在线观看 | 国产精品亚洲五月天高清 | 人妻无码久久精品人妻 | 2020久久超碰国产精品最新 | 亚洲中文字幕在线无码一区二区 | 国产精品无套呻吟在线 | 精品国产乱码久久久久乱码 | 国产成人无码a区在线观看视频app | 国产97色在线 | 免 | 疯狂三人交性欧美 | 性啪啪chinese东北女人 | 天干天干啦夜天干天2017 | 国产舌乚八伦偷品w中 | 国产美女极度色诱视频www | 日韩视频 中文字幕 视频一区 | 宝宝好涨水快流出来免费视频 | 色综合久久久无码中文字幕 | 亚洲国产高清在线观看视频 | 色婷婷综合激情综在线播放 | 国产精品久久久午夜夜伦鲁鲁 | 成人免费视频一区二区 | 欧美老熟妇乱xxxxx | 特黄特色大片免费播放器图片 | 中文精品无码中文字幕无码专区 | 久久97精品久久久久久久不卡 | 黑森林福利视频导航 | 日日橹狠狠爱欧美视频 | 久久久久久av无码免费看大片 | 亚洲自偷自拍另类第1页 | 特级做a爰片毛片免费69 | 国产午夜亚洲精品不卡 | 日韩av无码一区二区三区 | 妺妺窝人体色www在线小说 | 午夜熟女插插xx免费视频 | 98国产精品综合一区二区三区 | 亚洲午夜福利在线观看 | 欧美 丝袜 自拍 制服 另类 | 九九热爱视频精品 | 天堂在线观看www | 一本无码人妻在中文字幕免费 | 久久久www成人免费毛片 | 日韩成人一区二区三区在线观看 | 国产精品久久福利网站 | 免费无码的av片在线观看 | 99久久婷婷国产综合精品青草免费 | 国产午夜福利100集发布 | 动漫av网站免费观看 | 又色又爽又黄的美女裸体网站 | 中文字幕乱码人妻无码久久 | 狠狠色欧美亚洲狠狠色www | 强辱丰满人妻hd中文字幕 | 国产精品久久久午夜夜伦鲁鲁 | 亚洲熟妇自偷自拍另类 | 国产婷婷色一区二区三区在线 | 国产av一区二区精品久久凹凸 | 午夜男女很黄的视频 | 精品日本一区二区三区在线观看 | 亚洲va中文字幕无码久久不卡 | 日本一区二区更新不卡 | 亚洲色欲色欲欲www在线 | 学生妹亚洲一区二区 | 丝袜人妻一区二区三区 | 婷婷六月久久综合丁香 | 无遮挡国产高潮视频免费观看 | 国产精品免费大片 | www国产精品内射老师 | 亚洲欧洲无卡二区视頻 | 国产精品丝袜黑色高跟鞋 | 欧美国产亚洲日韩在线二区 | 牲欲强的熟妇农村老妇女 | 丰满少妇熟乱xxxxx视频 | 国产偷抇久久精品a片69 | 国产精品igao视频网 | 九一九色国产 | 强开小婷嫩苞又嫩又紧视频 | 欧美肥老太牲交大战 | 国产真人无遮挡作爱免费视频 | 内射巨臀欧美在线视频 | 麻豆国产人妻欲求不满谁演的 | 国产精品自产拍在线观看 | 成熟妇人a片免费看网站 | 无码人妻av免费一区二区三区 | 亚洲大尺度无码无码专区 | yw尤物av无码国产在线观看 | 色婷婷综合激情综在线播放 | 国产女主播喷水视频在线观看 | 任你躁国产自任一区二区三区 | 久久99精品久久久久婷婷 | 成人毛片一区二区 | 精品偷拍一区二区三区在线看 | 天天av天天av天天透 | 国产亚洲人成a在线v网站 | 国内综合精品午夜久久资源 | 男人扒开女人内裤强吻桶进去 | 曰韩少妇内射免费播放 | 欧美人与禽zoz0性伦交 | 久久精品人妻少妇一区二区三区 | 国内精品人妻无码久久久影院蜜桃 | 亚洲狠狠婷婷综合久久 | 伊人久久大香线焦av综合影院 | 国产精品嫩草久久久久 | 欧美日韩人成综合在线播放 | 青青久在线视频免费观看 | 久久综合色之久久综合 | 国产亚洲精品久久久久久国模美 | 成人无码精品一区二区三区 | 久久99精品久久久久久动态图 | 日韩无码专区 | 最新国产乱人伦偷精品免费网站 | 日韩少妇内射免费播放 | 国产午夜视频在线观看 | 亚洲欧美日韩国产精品一区二区 | 激情综合激情五月俺也去 | 在教室伦流澡到高潮hnp视频 | 少妇厨房愉情理9仑片视频 | 99riav国产精品视频 | 麻豆国产人妻欲求不满谁演的 | 亚洲国产成人av在线观看 | 亚洲午夜无码久久 | 色综合视频一区二区三区 | 日本xxxx色视频在线观看免费 | 国内精品久久毛片一区二区 | 色综合视频一区二区三区 | 黑森林福利视频导航 | 久久天天躁夜夜躁狠狠 | 日韩人妻无码一区二区三区久久99 | 白嫩日本少妇做爰 | 日本精品久久久久中文字幕 | 国产又粗又硬又大爽黄老大爷视 | 国产成人无码av在线影院 | 国内精品人妻无码久久久影院 | 日韩亚洲欧美精品综合 | 丰满肥臀大屁股熟妇激情视频 | 久久 国产 尿 小便 嘘嘘 | 两性色午夜视频免费播放 | 又粗又大又硬又长又爽 | 扒开双腿疯狂进出爽爽爽视频 | 亚洲欧美精品伊人久久 | 成在人线av无码免观看麻豆 | 国内精品一区二区三区不卡 | 中文字幕人妻丝袜二区 | 我要看www免费看插插视频 | 国产av无码专区亚洲awww | 午夜无码人妻av大片色欲 | 老熟妇仑乱视频一区二区 | 国产网红无码精品视频 | 成人免费视频视频在线观看 免费 | √8天堂资源地址中文在线 | 国产成人精品视频ⅴa片软件竹菊 | 内射后入在线观看一区 | 捆绑白丝粉色jk震动捧喷白浆 | 日日夜夜撸啊撸 | 97se亚洲精品一区 | 丰满少妇人妻久久久久久 | 国产小呦泬泬99精品 | 久久 国产 尿 小便 嘘嘘 | 亚洲中文字幕在线无码一区二区 | 日日麻批免费40分钟无码 | 欧美一区二区三区视频在线观看 | 娇妻被黑人粗大高潮白浆 | 99久久99久久免费精品蜜桃 | 欧美成人家庭影院 | 精品无码国产一区二区三区av | 奇米影视7777久久精品人人爽 | 中文字幕无码人妻少妇免费 | 亚洲理论电影在线观看 | 俺去俺来也www色官网 | 综合激情五月综合激情五月激情1 | 久久亚洲中文字幕无码 | 欧美变态另类xxxx | 国产精品丝袜黑色高跟鞋 | 99久久婷婷国产综合精品青草免费 | 久久国产自偷自偷免费一区调 | 日本一区二区三区免费高清 | 色 综合 欧美 亚洲 国产 | 乱码午夜-极国产极内射 | 日本免费一区二区三区最新 | 欧美放荡的少妇 | 精品国产一区二区三区av 性色 | 成人免费无码大片a毛片 | 人妻无码久久精品人妻 | 激情内射日本一区二区三区 | 色狠狠av一区二区三区 | 一本加勒比波多野结衣 | 性啪啪chinese东北女人 | 熟妇激情内射com | 任你躁国产自任一区二区三区 | 国产成人精品无码播放 | 日本熟妇人妻xxxxx人hd | 国产高清不卡无码视频 | 欧美一区二区三区视频在线观看 | 人人爽人人爽人人片av亚洲 | 精品国产精品久久一区免费式 | 兔费看少妇性l交大片免费 | 内射爽无广熟女亚洲 | 少妇一晚三次一区二区三区 | 色欲人妻aaaaaaa无码 | 无码av中文字幕免费放 | 东京一本一道一二三区 | 久久午夜无码鲁丝片秋霞 | 爆乳一区二区三区无码 | 国精产品一品二品国精品69xx | 亚洲色www成人永久网址 | 国产亚洲欧美日韩亚洲中文色 | 精品久久8x国产免费观看 | 国产av人人夜夜澡人人爽麻豆 | 国内精品九九久久久精品 | 男女猛烈xx00免费视频试看 | 国产办公室秘书无码精品99 | 日产精品99久久久久久 | 大肉大捧一进一出好爽视频 | 伊人久久婷婷五月综合97色 | 天天拍夜夜添久久精品大 | 久久综合九色综合97网 | 国产成人av免费观看 | 国产精品人妻一区二区三区四 | 亚洲日韩乱码中文无码蜜桃臀网站 | 国产va免费精品观看 | 国产精品久久国产精品99 | 1000部啪啪未满十八勿入下载 | 欧美激情综合亚洲一二区 | 国产免费久久精品国产传媒 | 亚洲日本va中文字幕 | 久久 国产 尿 小便 嘘嘘 | 亚洲色欲色欲天天天www | 一本大道久久东京热无码av | 亚洲色欲色欲欲www在线 | 在线观看国产一区二区三区 | 亚洲成a人一区二区三区 | 熟女体下毛毛黑森林 | 东京热无码av男人的天堂 | 久精品国产欧美亚洲色aⅴ大片 | 中文字幕日韩精品一区二区三区 | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 亚洲一区二区观看播放 | 在线看片无码永久免费视频 | 成人三级无码视频在线观看 | 天天躁日日躁狠狠躁免费麻豆 | 300部国产真实乱 | 东京无码熟妇人妻av在线网址 | 无套内谢的新婚少妇国语播放 | 乱人伦中文视频在线观看 | 无码吃奶揉捏奶头高潮视频 | 亚洲色在线无码国产精品不卡 | 日本一区二区三区免费播放 | 无码人妻av免费一区二区三区 | 香蕉久久久久久av成人 | 色偷偷人人澡人人爽人人模 | 午夜丰满少妇性开放视频 | 无码av岛国片在线播放 | 在线欧美精品一区二区三区 | 牲欲强的熟妇农村老妇女视频 | 欧美日韩色另类综合 | 亚洲成av人影院在线观看 | 免费播放一区二区三区 | 国产热a欧美热a在线视频 | 俺去俺来也www色官网 | 亚洲理论电影在线观看 | 色综合视频一区二区三区 | 97精品国产97久久久久久免费 | 一本大道久久东京热无码av | www国产精品内射老师 | 少妇高潮一区二区三区99 | 亚洲中文字幕乱码av波多ji | 成人精品一区二区三区中文字幕 | 国产特级毛片aaaaaa高潮流水 | 国产精品美女久久久 | 久久人人97超碰a片精品 | 国产在热线精品视频 | 波多野结衣 黑人 | 亚洲国产日韩a在线播放 | 亚洲大尺度无码无码专区 | 亚洲欧美日韩国产精品一区二区 | 激情爆乳一区二区三区 | 国产熟妇另类久久久久 | 国产免费无码一区二区视频 | 人妻少妇精品无码专区二区 | 中文久久乱码一区二区 | 国产国产精品人在线视 | 亚洲娇小与黑人巨大交 | 18禁黄网站男男禁片免费观看 | 67194成是人免费无码 | 日本在线高清不卡免费播放 | 2019nv天堂香蕉在线观看 | 国产成人久久精品流白浆 | 午夜福利不卡在线视频 | 无码精品国产va在线观看dvd | 女人被男人躁得好爽免费视频 | 欧美三级不卡在线观看 | 丰满妇女强制高潮18xxxx | 樱花草在线社区www | 国产精品18久久久久久麻辣 | а√资源新版在线天堂 | 色综合久久88色综合天天 | 亚洲国产成人a精品不卡在线 | 亚洲色欲色欲欲www在线 | 性生交大片免费看l | 99在线 | 亚洲 | 蜜臀av在线观看 在线欧美精品一区二区三区 | 国产精品久久久久无码av色戒 | 国产极品视觉盛宴 | 天堂无码人妻精品一区二区三区 | 久久久久久久人妻无码中文字幕爆 | 国产成人精品一区二区在线小狼 | 免费人成在线观看网站 | 亚洲成av人影院在线观看 | 夜先锋av资源网站 | 中文字幕人妻丝袜二区 | 天堂а√在线中文在线 | 日本饥渴人妻欲求不满 | 欧美成人午夜精品久久久 | 日韩无套无码精品 | 亚洲成av人影院在线观看 | 日日摸夜夜摸狠狠摸婷婷 | 国产成人无码a区在线观看视频app | 影音先锋中文字幕无码 | 亚洲精品午夜国产va久久成人 | 国产乡下妇女做爰 | 初尝人妻少妇中文字幕 | 图片区 小说区 区 亚洲五月 | 国产亚洲精品久久久久久久 | 国产人妻精品一区二区三区 | 99视频精品全部免费免费观看 | 国产香蕉97碰碰久久人人 | 欧美人与动性行为视频 | 牲交欧美兽交欧美 | 亚洲成在人网站无码天堂 | 好爽又高潮了毛片免费下载 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 国产 精品 自在自线 | 好屌草这里只有精品 | 亚洲经典千人经典日产 | 久久综合九色综合97网 | 日本熟妇乱子伦xxxx | 亚洲男女内射在线播放 | 六月丁香婷婷色狠狠久久 | 2020最新国产自产精品 | 欧美性生交xxxxx久久久 | 在线观看国产午夜福利片 | 乱人伦人妻中文字幕无码久久网 | 日日摸日日碰夜夜爽av | 国产人妻人伦精品 | 久久精品女人天堂av免费观看 | 国产又爽又黄又刺激的视频 | 国产精品美女久久久网av | 精品偷拍一区二区三区在线看 | 欧美老人巨大xxxx做受 | 99精品无人区乱码1区2区3区 | 成人无码精品1区2区3区免费看 | 午夜理论片yy44880影院 | 大地资源中文第3页 | 丝袜 中出 制服 人妻 美腿 | 国产三级精品三级男人的天堂 | 久久午夜无码鲁丝片 | 国产精品亚洲五月天高清 | 亚洲精品中文字幕久久久久 | 欧美日本免费一区二区三区 | 国产成人一区二区三区别 | 欧美肥老太牲交大战 | 精品久久久久久亚洲精品 | 一本久久a久久精品亚洲 | 熟女体下毛毛黑森林 | 欧美日韩人成综合在线播放 | 久久精品国产99精品亚洲 | 欧美成人高清在线播放 | 国产精品无码一区二区三区不卡 | 亚欧洲精品在线视频免费观看 | 色婷婷综合激情综在线播放 | 无遮挡国产高潮视频免费观看 | 亚洲熟熟妇xxxx | 亚洲一区av无码专区在线观看 | 天堂亚洲免费视频 | 亚洲国产精华液网站w | 亚欧洲精品在线视频免费观看 | 精品无码一区二区三区的天堂 | 图片区 小说区 区 亚洲五月 | 国产av无码专区亚洲a∨毛片 | av人摸人人人澡人人超碰下载 | 秋霞成人午夜鲁丝一区二区三区 | 久久久久久国产精品无码下载 | 久久精品国产精品国产精品污 | 国产亚洲精品久久久ai换 | 人人爽人人爽人人片av亚洲 | 亚洲中文字幕无码中文字在线 | 国产超碰人人爽人人做人人添 | 午夜时刻免费入口 | 国产婷婷色一区二区三区在线 | 欧美日韩在线亚洲综合国产人 | 99久久婷婷国产综合精品青草免费 | 无码毛片视频一区二区本码 | 对白脏话肉麻粗话av | 久久精品国产一区二区三区 | 男人扒开女人内裤强吻桶进去 | 亚洲人成网站在线播放942 | 国产成人人人97超碰超爽8 | 国产精品久久久av久久久 | 亚洲 日韩 欧美 成人 在线观看 | 荫蒂被男人添的好舒服爽免费视频 | 国产成人精品一区二区在线小狼 | ass日本丰满熟妇pics | 亚洲国产午夜精品理论片 | 久久久久久久久蜜桃 | 成人性做爰aaa片免费看 | av无码久久久久不卡免费网站 | 18禁黄网站男男禁片免费观看 | 精品久久久久久人妻无码中文字幕 | 在线天堂新版最新版在线8 | aa片在线观看视频在线播放 | 日本精品人妻无码免费大全 | 国产午夜福利100集发布 | 欧美变态另类xxxx | 99久久人妻精品免费二区 | 欧美三级不卡在线观看 | √天堂资源地址中文在线 | 日本丰满熟妇videos | 国产舌乚八伦偷品w中 | 强伦人妻一区二区三区视频18 | 亚洲成av人在线观看网址 | 免费国产黄网站在线观看 | 精品无码一区二区三区爱欲 | 精品一二三区久久aaa片 | 永久黄网站色视频免费直播 | 免费人成在线视频无码 | 欧美变态另类xxxx | 成人av无码一区二区三区 | 国产成人午夜福利在线播放 | 国产精品a成v人在线播放 | 欧美日本日韩 | 丝袜 中出 制服 人妻 美腿 | 少妇被黑人到高潮喷出白浆 | 亚洲日韩中文字幕在线播放 | 日韩精品乱码av一区二区 | 午夜福利一区二区三区在线观看 | 乌克兰少妇xxxx做受 | 国产成人精品一区二区在线小狼 | 国产亚洲人成a在线v网站 | 九月婷婷人人澡人人添人人爽 | 熟女少妇在线视频播放 | 亚洲理论电影在线观看 | 欧美性猛交内射兽交老熟妇 | 亚洲午夜久久久影院 | 亚洲无人区一区二区三区 | 国内少妇偷人精品视频 | 国产精品久久久久影院嫩草 | 日韩在线不卡免费视频一区 | 亚洲欧美中文字幕5发布 | 激情内射日本一区二区三区 | 久久国产精品二国产精品 | 在线观看国产一区二区三区 | 狠狠色欧美亚洲狠狠色www | 99久久精品无码一区二区毛片 | 中文无码精品a∨在线观看不卡 | 日本一区二区更新不卡 | 国产精品久久久久9999小说 | 综合人妻久久一区二区精品 | 熟妇人妻无乱码中文字幕 | 双乳奶水饱满少妇呻吟 | 99久久精品无码一区二区毛片 | 奇米影视7777久久精品 | 日韩精品乱码av一区二区 | 免费无码的av片在线观看 | 久久久久成人片免费观看蜜芽 | 久久综合九色综合欧美狠狠 | 国产午夜精品一区二区三区嫩草 | 人妻无码久久精品人妻 | 亚洲精品中文字幕久久久久 | 久久久精品人妻久久影视 | 国产绳艺sm调教室论坛 | 人妻无码αv中文字幕久久琪琪布 | 精品成人av一区二区三区 | 又大又黄又粗又爽的免费视频 | av小次郎收藏 | 99riav国产精品视频 | 久久精品99久久香蕉国产色戒 | 欧美日韩精品 | 日韩 欧美 动漫 国产 制服 | 日日橹狠狠爱欧美视频 | 亚洲va中文字幕无码久久不卡 | 国产欧美熟妇另类久久久 | 亚洲精品鲁一鲁一区二区三区 | 国产网红无码精品视频 | 夜精品a片一区二区三区无码白浆 | 国产热a欧美热a在线视频 | 精品无码国产一区二区三区av | 国内老熟妇对白xxxxhd | 日韩成人一区二区三区在线观看 | 一本一道久久综合久久 | 久久久精品456亚洲影院 | 午夜免费福利小电影 | 国产亚洲欧美在线专区 | 欧美大屁股xxxxhd黑色 | 激情内射日本一区二区三区 | 国产高清av在线播放 | 久久这里只有精品视频9 | 亚洲国产av精品一区二区蜜芽 | 国产亚洲人成a在线v网站 | 99精品久久毛片a片 | 国产综合久久久久鬼色 | 亚洲狠狠婷婷综合久久 | 久久精品国产日本波多野结衣 | 天堂在线观看www | 国产真人无遮挡作爱免费视频 | 日本在线高清不卡免费播放 | 永久免费观看美女裸体的网站 | 久久zyz资源站无码中文动漫 | 精品人妻人人做人人爽夜夜爽 | 黄网在线观看免费网站 | 国产av人人夜夜澡人人爽麻豆 | 久在线观看福利视频 | 亚洲精品一区三区三区在线观看 | 又紧又大又爽精品一区二区 | 国产农村妇女高潮大叫 | 18禁黄网站男男禁片免费观看 | 学生妹亚洲一区二区 | 婷婷六月久久综合丁香 | 亚洲国产精品无码久久久久高潮 | 夜精品a片一区二区三区无码白浆 | 内射老妇bbwx0c0ck | 欧美熟妇另类久久久久久多毛 | 老熟女重囗味hdxx69 | 中文无码精品a∨在线观看不卡 | 狠狠cao日日穞夜夜穞av | 日韩欧美中文字幕在线三区 | 2020久久超碰国产精品最新 | 久久婷婷五月综合色国产香蕉 | 精品日本一区二区三区在线观看 | 久久午夜夜伦鲁鲁片无码免费 | 色综合久久网 | 精品无人国产偷自产在线 | 天天摸天天透天天添 | 老熟女重囗味hdxx69 | 无码福利日韩神码福利片 | 天天摸天天透天天添 | 77777熟女视频在线观看 а天堂中文在线官网 | 久久久亚洲欧洲日产国码αv | 国产疯狂伦交大片 | 伊人久久婷婷五月综合97色 | 亚洲中文字幕在线无码一区二区 | 国产真人无遮挡作爱免费视频 | 精品日本一区二区三区在线观看 | 麻花豆传媒剧国产免费mv在线 | 成人试看120秒体验区 | 秋霞成人午夜鲁丝一区二区三区 | 国产精品久久久久久久9999 | 午夜福利一区二区三区在线观看 | 亚洲综合在线一区二区三区 | 未满小14洗澡无码视频网站 | 日韩精品无码一区二区中文字幕 | 国产99久久精品一区二区 | 1000部夫妻午夜免费 | 水蜜桃亚洲一二三四在线 | 久久www免费人成人片 | 啦啦啦www在线观看免费视频 | 国产又爽又猛又粗的视频a片 | 午夜无码人妻av大片色欲 | 精品无码国产自产拍在线观看蜜 | 国产精品久久久久久久9999 | 国产一区二区三区日韩精品 | 国产精品亚洲一区二区三区喷水 | 精品一区二区三区无码免费视频 | 激情亚洲一区国产精品 | 无码帝国www无码专区色综合 | 欧美日韩一区二区免费视频 | 色欲综合久久中文字幕网 | 爽爽影院免费观看 | 成人无码视频免费播放 | 亚洲成色www久久网站 | 欧美野外疯狂做受xxxx高潮 | 亚洲中文字幕在线无码一区二区 | 日日天日日夜日日摸 | 人妻人人添人妻人人爱 | 国产激情精品一区二区三区 | 日韩av无码一区二区三区 | 无码av免费一区二区三区试看 | 亚洲呦女专区 | 久久综合九色综合欧美狠狠 | 午夜精品久久久内射近拍高清 | 欧美喷潮久久久xxxxx | 18精品久久久无码午夜福利 | 麻豆国产人妻欲求不满谁演的 | 国产热a欧美热a在线视频 | 狠狠综合久久久久综合网 | 色婷婷香蕉在线一区二区 | 婷婷五月综合激情中文字幕 | 亚洲欧洲无卡二区视頻 | 成人精品视频一区二区 | 亚洲第一无码av无码专区 | 夜精品a片一区二区三区无码白浆 | 精品偷拍一区二区三区在线看 | 欧洲熟妇精品视频 | 免费无码一区二区三区蜜桃大 | 午夜免费福利小电影 | 成人精品视频一区二区三区尤物 | 真人与拘做受免费视频 | 日本欧美一区二区三区乱码 | 欧美精品在线观看 | 激情人妻另类人妻伦 | 亚洲乱码中文字幕在线 | 嫩b人妻精品一区二区三区 | 大肉大捧一进一出好爽视频 | 呦交小u女精品视频 | 成年美女黄网站色大免费全看 | 老熟女重囗味hdxx69 | 伊人久久大香线蕉av一区二区 | 成人精品视频一区二区三区尤物 | 香港三级日本三级妇三级 | 亚洲欧美精品伊人久久 | 国产成人人人97超碰超爽8 | 俺去俺来也在线www色官网 | а√资源新版在线天堂 | 国产国语老龄妇女a片 | 亚洲精品欧美二区三区中文字幕 | 永久黄网站色视频免费直播 | 荫蒂添的好舒服视频囗交 | √天堂中文官网8在线 | 亚洲午夜久久久影院 | 国产精华av午夜在线观看 | 欧美三级不卡在线观看 | 欧美 日韩 人妻 高清 中文 | 亚洲欧美日韩国产精品一区二区 | 国产精品久久久久影院嫩草 | 久久综合九色综合97网 | 青青草原综合久久大伊人精品 | 秋霞特色aa大片 | 3d动漫精品啪啪一区二区中 | 亚洲欧美国产精品久久 | 国产精品亚洲五月天高清 | 欧美阿v高清资源不卡在线播放 | 精品一区二区三区无码免费视频 | 夜夜影院未满十八勿进 | 欧洲精品码一区二区三区免费看 | 国产成人精品一区二区在线小狼 | 久久久久se色偷偷亚洲精品av | 精品人妻人人做人人爽 | 精品水蜜桃久久久久久久 | 高潮毛片无遮挡高清免费视频 | 国产激情无码一区二区app | 国产精品成人av在线观看 | 永久免费观看美女裸体的网站 | 久久这里只有精品视频9 | 欧美猛少妇色xxxxx | 国产精品永久免费视频 | 国产激情精品一区二区三区 | 女人被男人躁得好爽免费视频 | 青草青草久热国产精品 | 中文字幕人妻丝袜二区 | 思思久久99热只有频精品66 | 日韩精品无码一区二区中文字幕 | 国产精品人妻一区二区三区四 | 乱中年女人伦av三区 | 亚洲熟妇自偷自拍另类 | 99er热精品视频 | 欧美国产亚洲日韩在线二区 | 国产人妻精品一区二区三区 | 国産精品久久久久久久 | 亚洲性无码av中文字幕 | 国产热a欧美热a在线视频 | 国内少妇偷人精品视频 | 亚洲无人区一区二区三区 | 国产乱子伦视频在线播放 | 欧美国产亚洲日韩在线二区 | 国产色视频一区二区三区 | 99精品国产综合久久久久五月天 | www国产亚洲精品久久久日本 | 在线欧美精品一区二区三区 | 无码一区二区三区在线观看 | 丰满少妇弄高潮了www | 亚洲精品久久久久久一区二区 | 中文字幕+乱码+中文字幕一区 | 国产成人精品久久亚洲高清不卡 | 亚洲va中文字幕无码久久不卡 | 99久久久无码国产aaa精品 | 中文字幕中文有码在线 | 亚洲中文字幕av在天堂 | 99久久99久久免费精品蜜桃 | 无码人妻黑人中文字幕 | 亚洲精品成人av在线 | 夫妻免费无码v看片 | 人人妻人人澡人人爽精品欧美 | 国产亚洲精品久久久闺蜜 | 欧美乱妇无乱码大黄a片 | 成人动漫在线观看 | 免费无码午夜福利片69 | 亚洲 高清 成人 动漫 | 撕开奶罩揉吮奶头视频 | 日本免费一区二区三区最新 | 中国女人内谢69xxxxxa片 | 午夜男女很黄的视频 | 麻豆精品国产精华精华液好用吗 | 一本久道久久综合狠狠爱 | 呦交小u女精品视频 | 黑人粗大猛烈进出高潮视频 | 日韩人妻系列无码专区 | 欧美丰满熟妇xxxx性ppx人交 | 88国产精品欧美一区二区三区 | 精品午夜福利在线观看 | 乌克兰少妇性做爰 | 午夜精品一区二区三区的区别 | 性啪啪chinese东北女人 | 亚洲国精产品一二二线 | 成人女人看片免费视频放人 | 国产激情一区二区三区 | 亚洲国产高清在线观看视频 | 亚洲精品国产第一综合99久久 | 爱做久久久久久 | 欧美人与善在线com | 亚洲成a人一区二区三区 | 色综合久久88色综合天天 | 在教室伦流澡到高潮hnp视频 | 精品日本一区二区三区在线观看 | 亚洲精品一区二区三区大桥未久 | 成人一在线视频日韩国产 | 亚洲午夜久久久影院 | 性啪啪chinese东北女人 | 亚洲成色www久久网站 | 天天爽夜夜爽夜夜爽 | 国产综合在线观看 | 色婷婷综合激情综在线播放 | 亚洲国产精品一区二区美利坚 | 女人被爽到呻吟gif动态图视看 | 天天av天天av天天透 | 色噜噜亚洲男人的天堂 | 又黄又爽又色的视频 | 亚洲国产日韩a在线播放 | 欧洲vodafone精品性 | 内射老妇bbwx0c0ck | 狠狠色色综合网站 | 色一情一乱一伦一视频免费看 | 亚洲日韩精品欧美一区二区 | 色综合久久中文娱乐网 | 男人的天堂2018无码 | 日本在线高清不卡免费播放 | 国产精品亚洲lv粉色 | 国产一区二区三区精品视频 | 亚洲 a v无 码免 费 成 人 a v | 男女作爱免费网站 | www国产精品内射老师 | 中文字幕无码乱人伦 | 77777熟女视频在线观看 а天堂中文在线官网 | 亚洲爆乳精品无码一区二区三区 | 牲欲强的熟妇农村老妇女视频 | 亚洲精品久久久久avwww潮水 | 亚洲一区二区观看播放 | 午夜丰满少妇性开放视频 | 久久无码专区国产精品s | 国产猛烈高潮尖叫视频免费 | 青草青草久热国产精品 | 欧美午夜特黄aaaaaa片 | 性做久久久久久久免费看 | 美女黄网站人色视频免费国产 | 丰满人妻被黑人猛烈进入 | 熟妇人妻无乱码中文字幕 | 亚洲成a人片在线观看无码3d | 伊人久久婷婷五月综合97色 | 色婷婷av一区二区三区之红樱桃 | 牛和人交xxxx欧美 | 搡女人真爽免费视频大全 | 99久久婷婷国产综合精品青草免费 | 亚洲精品国产a久久久久久 | 2019午夜福利不卡片在线 | 久久国产自偷自偷免费一区调 | 亚洲区欧美区综合区自拍区 | 国产av无码专区亚洲awww | 成人精品天堂一区二区三区 | 67194成是人免费无码 | 欧美日本免费一区二区三区 | 色婷婷综合激情综在线播放 | 国产无遮挡又黄又爽免费视频 | 国产成人精品优优av | 中国女人内谢69xxxxxa片 | 国内丰满熟女出轨videos | 波多野结衣一区二区三区av免费 | 亚洲人成网站免费播放 | 久久精品丝袜高跟鞋 | 好爽又高潮了毛片免费下载 | 日韩精品无码一本二本三本色 | 大色综合色综合网站 | 一本久道久久综合婷婷五月 | 少妇久久久久久人妻无码 | 中文字幕无码av波多野吉衣 | 日韩欧美群交p片內射中文 | 久久天天躁狠狠躁夜夜免费观看 | 国产偷抇久久精品a片69 | 国内揄拍国内精品人妻 | 天天躁日日躁狠狠躁免费麻豆 | 麻豆果冻传媒2021精品传媒一区下载 | 亚洲国产高清在线观看视频 | 国产成人无码av在线影院 | 国产精品久久久一区二区三区 | 亚洲日韩中文字幕在线播放 | 99麻豆久久久国产精品免费 | 女人和拘做爰正片视频 | 欧美大屁股xxxxhd黑色 | 国产在线一区二区三区四区五区 | 国产又爽又猛又粗的视频a片 | 亚洲国产欧美日韩精品一区二区三区 | 日本熟妇人妻xxxxx人hd | 欧美 日韩 人妻 高清 中文 | 露脸叫床粗话东北少妇 | 国产成人无码av一区二区 | 四十如虎的丰满熟妇啪啪 | 野狼第一精品社区 | 欧美猛少妇色xxxxx | 久久久久人妻一区精品色欧美 | 国产 浪潮av性色四虎 | 国产美女精品一区二区三区 | 亚洲一区二区三区播放 | 国内老熟妇对白xxxxhd | 精品无码国产自产拍在线观看蜜 | 成人免费视频视频在线观看 免费 | 全球成人中文在线 | 久久99精品久久久久婷婷 | 性开放的女人aaa片 | 亚洲精品午夜无码电影网 | 精品熟女少妇av免费观看 | 久久国产精品萌白酱免费 | 黄网在线观看免费网站 | 少妇的肉体aa片免费 | 亚洲天堂2017无码中文 | 麻豆md0077饥渴少妇 | av人摸人人人澡人人超碰下载 | 欧美成人免费全部网站 | 国产成人一区二区三区在线观看 | 97无码免费人妻超级碰碰夜夜 | 亚洲 欧美 激情 小说 另类 | 国产亚洲精品久久久久久久 | 老熟妇仑乱视频一区二区 | 天天拍夜夜添久久精品大 | 成 人 免费观看网站 | 国内精品人妻无码久久久影院 | 无码国内精品人妻少妇 | 国产精品高潮呻吟av久久 | 婷婷六月久久综合丁香 | 清纯唯美经典一区二区 | 久久精品国产亚洲精品 | 日本一卡二卡不卡视频查询 | 亚洲性无码av中文字幕 | 国产艳妇av在线观看果冻传媒 | 曰韩少妇内射免费播放 | 特大黑人娇小亚洲女 | 国产sm调教视频在线观看 | yw尤物av无码国产在线观看 | 无遮无挡爽爽免费视频 | 国内揄拍国内精品人妻 | 2020久久超碰国产精品最新 | 亚洲国产精品一区二区第一页 | 亚洲欧美色中文字幕在线 | 日本乱偷人妻中文字幕 | 国产97色在线 | 免 | 免费无码一区二区三区蜜桃大 | 波多野结衣av在线观看 | 欧美乱妇无乱码大黄a片 | ass日本丰满熟妇pics | 自拍偷自拍亚洲精品10p | 在线观看国产午夜福利片 | 青草青草久热国产精品 | 日韩精品久久久肉伦网站 | 久热国产vs视频在线观看 | 无码人妻丰满熟妇区五十路百度 | 2020久久香蕉国产线看观看 | 欧美精品在线观看 | www国产亚洲精品久久久日本 | 亚洲国产成人a精品不卡在线 | 水蜜桃色314在线观看 | 国内老熟妇对白xxxxhd | 亚洲国产成人a精品不卡在线 | 精品国产一区二区三区四区 | 2019午夜福利不卡片在线 | 国产人妻久久精品二区三区老狼 | 天天av天天av天天透 | 国内精品九九久久久精品 | 极品嫩模高潮叫床 | 亚洲gv猛男gv无码男同 | 玩弄中年熟妇正在播放 | 国产真实乱对白精彩久久 | 国产人妻精品午夜福利免费 | 秋霞特色aa大片 | 色综合视频一区二区三区 | 欧美老人巨大xxxx做受 | 十八禁视频网站在线观看 | 无码中文字幕色专区 | 亚洲一区二区三区香蕉 | 免费人成网站视频在线观看 | 玩弄中年熟妇正在播放 | 疯狂三人交性欧美 | 波多野结衣 黑人 | 久久国产精品二国产精品 | 国产成人无码区免费内射一片色欲 | 成 人 免费观看网站 | 牲欲强的熟妇农村老妇女视频 | 国产情侣作爱视频免费观看 | 日本丰满熟妇videos | 色五月丁香五月综合五月 | 久久精品国产99精品亚洲 | 一本久道高清无码视频 | 大地资源中文第3页 | 水蜜桃av无码 | 丰满肥臀大屁股熟妇激情视频 | 国产亚洲精品久久久ai换 | 亚洲精品午夜无码电影网 | 国产免费观看黄av片 | 无码国产乱人伦偷精品视频 | 人人妻人人澡人人爽欧美一区 | 无码国内精品人妻少妇 | 精品无人国产偷自产在线 | 无码午夜成人1000部免费视频 | 久久综合香蕉国产蜜臀av | 中文无码精品a∨在线观看不卡 | 日本一区二区更新不卡 | 丝袜足控一区二区三区 | а√资源新版在线天堂 | 国产激情综合五月久久 | 亚洲无人区午夜福利码高清完整版 | 亚洲精品一区二区三区四区五区 | 久久久久国色av免费观看性色 | 在线播放免费人成毛片乱码 | 人人妻人人澡人人爽欧美精品 | 国产成人精品三级麻豆 | 久久久久免费看成人影片 | 人人澡人人妻人人爽人人蜜桃 | www一区二区www免费 | 无码av最新清无码专区吞精 | 亚洲精品久久久久中文第一幕 | 亚洲成av人综合在线观看 | 女人和拘做爰正片视频 | 又色又爽又黄的美女裸体网站 | 俺去俺来也在线www色官网 | 久久99精品国产.久久久久 | 四虎4hu永久免费 | 2020久久香蕉国产线看观看 | 97夜夜澡人人爽人人喊中国片 | 国产一区二区三区影院 | 奇米影视7777久久精品人人爽 | 亚洲午夜福利在线观看 | av人摸人人人澡人人超碰下载 | 99久久精品日本一区二区免费 | 无码人妻少妇伦在线电影 | 狠狠色欧美亚洲狠狠色www | 任你躁国产自任一区二区三区 | 国产香蕉尹人视频在线 | 人人澡人人妻人人爽人人蜜桃 | 综合人妻久久一区二区精品 | 亚洲色欲色欲欲www在线 | 亚洲熟妇色xxxxx亚洲 | 亚洲国产精品久久久天堂 | 一本一道久久综合久久 | 99久久人妻精品免费一区 | 亚洲国产精品无码一区二区三区 | 久久亚洲精品中文字幕无男同 | 亚洲人成网站免费播放 | 亚洲s码欧洲m码国产av | 精品偷拍一区二区三区在线看 | 国产绳艺sm调教室论坛 | 婷婷五月综合激情中文字幕 | 秋霞特色aa大片 | 色爱情人网站 | 欧美日韩久久久精品a片 | 亚洲精品午夜国产va久久成人 | 国产无遮挡又黄又爽免费视频 | 亚洲自偷精品视频自拍 | 999久久久国产精品消防器材 | 日日摸日日碰夜夜爽av | 色妞www精品免费视频 | а天堂中文在线官网 | 日韩人妻无码一区二区三区久久99 | 秋霞成人午夜鲁丝一区二区三区 | 亚洲人成网站在线播放942 | 亚洲国产一区二区三区在线观看 | 久久精品一区二区三区四区 | 亚洲国产精品无码一区二区三区 | 久久人人爽人人爽人人片ⅴ | 欧美日韩人成综合在线播放 | 亚洲自偷精品视频自拍 | www国产精品内射老师 | 18禁止看的免费污网站 | 两性色午夜视频免费播放 | 无码中文字幕色专区 | 无码人妻精品一区二区三区下载 | 国产成人久久精品流白浆 | 亚洲中文字幕在线无码一区二区 | 亚洲成a人片在线观看日本 | 亚洲国产成人a精品不卡在线 | 无码人妻丰满熟妇区毛片18 | 欧美日韩视频无码一区二区三 | 亚洲性无码av中文字幕 | 熟妇女人妻丰满少妇中文字幕 | 色综合久久久久综合一本到桃花网 | 在线天堂新版最新版在线8 | 免费观看的无遮挡av | 色综合久久88色综合天天 | 色欲av亚洲一区无码少妇 | 国产午夜精品一区二区三区嫩草 | 色婷婷久久一区二区三区麻豆 | 国产成人无码一二三区视频 | 亚洲最大成人网站 | 国产人妻精品一区二区三区不卡 | 国产真人无遮挡作爱免费视频 | 亚洲欧洲中文日韩av乱码 | 在教室伦流澡到高潮hnp视频 | 人人妻人人澡人人爽欧美精品 | 夫妻免费无码v看片 | 国产成人无码a区在线观看视频app | 成人欧美一区二区三区黑人 | 欧美成人午夜精品久久久 | 国产精品成人av在线观看 | 荫蒂添的好舒服视频囗交 | aⅴ亚洲 日韩 色 图网站 播放 | 亚洲精品国偷拍自产在线麻豆 | 中文字幕 亚洲精品 第1页 | 久久婷婷五月综合色国产香蕉 | 日日天干夜夜狠狠爱 | av无码久久久久不卡免费网站 | 国产在热线精品视频 | 精品国产麻豆免费人成网站 | 国产美女精品一区二区三区 | 青青青爽视频在线观看 | 久久午夜无码鲁丝片午夜精品 | 人人妻人人澡人人爽欧美一区 | 荫蒂添的好舒服视频囗交 | ass日本丰满熟妇pics | 人妻有码中文字幕在线 | 色欲av亚洲一区无码少妇 | 国产精品久久久久9999小说 | 国产精品爱久久久久久久 | 99国产欧美久久久精品 | 亚洲欧美色中文字幕在线 | 无码av免费一区二区三区试看 | 中文字幕无码乱人伦 | 亚洲va欧美va天堂v国产综合 | 国产精品鲁鲁鲁 | 亚洲日韩av一区二区三区中文 | 中文字幕+乱码+中文字幕一区 | 日韩亚洲欧美中文高清在线 | 日韩精品成人一区二区三区 | 欧美野外疯狂做受xxxx高潮 | 午夜精品一区二区三区的区别 | 色婷婷综合中文久久一本 | 东京无码熟妇人妻av在线网址 | 精品一区二区三区波多野结衣 | 在线看片无码永久免费视频 | 亚洲一区二区三区 | 亚洲精品一区二区三区四区五区 | 中文字幕无码免费久久99 | 久久国产劲爆∧v内射 | 国产九九九九九九九a片 | 欧美丰满少妇xxxx性 | 色婷婷久久一区二区三区麻豆 | 精品厕所偷拍各类美女tp嘘嘘 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 日韩成人一区二区三区在线观看 | 成熟人妻av无码专区 | 国产又粗又硬又大爽黄老大爷视 | 久久精品国产99久久6动漫 | 日本成熟视频免费视频 | 亚洲精品美女久久久久久久 | 亚洲区欧美区综合区自拍区 | 成人无码精品一区二区三区 | 99久久99久久免费精品蜜桃 | 亚洲成av人片在线观看无码不卡 | 中文精品久久久久人妻不卡 | 久久久久久久久蜜桃 | 精品成人av一区二区三区 | 性欧美熟妇videofreesex | 88国产精品欧美一区二区三区 | 日韩人妻无码一区二区三区久久99 | 亚洲va中文字幕无码久久不卡 | 日韩精品一区二区av在线 | 日韩精品无码一区二区中文字幕 | 97久久精品无码一区二区 | 狠狠cao日日穞夜夜穞av | 日本精品人妻无码77777 天堂一区人妻无码 | 久久天天躁狠狠躁夜夜免费观看 | www一区二区www免费 | 亚洲一区二区观看播放 | 亚洲乱码国产乱码精品精 | 鲁鲁鲁爽爽爽在线视频观看 | 国产偷抇久久精品a片69 | 免费看少妇作爱视频 | 国产激情无码一区二区 | 精品人人妻人人澡人人爽人人 | 成在人线av无码免观看麻豆 | 日日干夜夜干 | 国产绳艺sm调教室论坛 | 亚洲精品一区二区三区在线 | 强辱丰满人妻hd中文字幕 | 双乳奶水饱满少妇呻吟 | 扒开双腿疯狂进出爽爽爽视频 | 久久久久亚洲精品中文字幕 | 亚洲乱码中文字幕在线 | 亚洲毛片av日韩av无码 | www国产亚洲精品久久网站 | 亚洲精品一区二区三区婷婷月 | 亚洲七七久久桃花影院 | 欧美色就是色 | 国产一区二区三区影院 | 少妇激情av一区二区 | 国内精品久久久久久中文字幕 | 国产三级久久久精品麻豆三级 | 亚洲成av人影院在线观看 | 精品无码国产自产拍在线观看蜜 | 久久久久se色偷偷亚洲精品av | 人妻少妇精品无码专区二区 | 国产性生交xxxxx无码 | 欧美国产亚洲日韩在线二区 | 久久久久se色偷偷亚洲精品av | 精品欧美一区二区三区久久久 | 久久伊人色av天堂九九小黄鸭 | 帮老师解开蕾丝奶罩吸乳网站 | 999久久久国产精品消防器材 | 日本va欧美va欧美va精品 | 一本无码人妻在中文字幕免费 | 又大又硬又黄的免费视频 | 无码人中文字幕 | 免费观看又污又黄的网站 | 国产精品成人av在线观看 | 麻豆精产国品 | 波多野结衣av在线观看 | 一二三四在线观看免费视频 | 国产在线无码精品电影网 | 性生交大片免费看女人按摩摩 | 免费观看又污又黄的网站 | 熟女少妇人妻中文字幕 | 国产精品久久久久久亚洲影视内衣 | 无码人妻精品一区二区三区下载 | 国产高清av在线播放 | 丰满人妻精品国产99aⅴ | 日韩精品一区二区av在线 | 久久zyz资源站无码中文动漫 | 色妞www精品免费视频 | 久久国内精品自在自线 | 高潮毛片无遮挡高清免费视频 | 男女性色大片免费网站 | 日韩成人一区二区三区在线观看 | av无码久久久久不卡免费网站 | 久久久久久av无码免费看大片 | 激情爆乳一区二区三区 | 国产xxx69麻豆国语对白 | 激情国产av做激情国产爱 | 曰韩少妇内射免费播放 | 国内精品人妻无码久久久影院 | 国产人妻精品一区二区三区不卡 | 日本一区二区三区免费高清 | 国产农村乱对白刺激视频 | 国产精品毛片一区二区 | 国内精品一区二区三区不卡 | 亚洲精品欧美二区三区中文字幕 | 国内揄拍国内精品少妇国语 | 亚洲国产综合无码一区 | 国产精品对白交换视频 | 国产成人无码av在线影院 | 国产suv精品一区二区五 | 又湿又紧又大又爽a视频国产 | 免费无码一区二区三区蜜桃大 | 少妇无套内谢久久久久 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 欧美人与善在线com | 熟妇人妻无码xxx视频 | 成人亚洲精品久久久久 | 窝窝午夜理论片影院 | 老司机亚洲精品影院 | 日韩人妻少妇一区二区三区 | 亚洲а∨天堂久久精品2021 | 美女张开腿让人桶 | 中文毛片无遮挡高清免费 | 欧美日韩综合一区二区三区 | 国产做国产爱免费视频 | 国产高清不卡无码视频 | 国产午夜视频在线观看 | 高潮喷水的毛片 | 国产精品久久久久影院嫩草 | 黄网在线观看免费网站 | 婷婷丁香五月天综合东京热 | 亚洲中文字幕av在天堂 | 精品久久8x国产免费观看 | 精品偷拍一区二区三区在线看 | 国产偷抇久久精品a片69 | 国产精品亚洲一区二区三区喷水 | 中文字幕无码日韩专区 | 日本www一道久久久免费榴莲 | 色婷婷香蕉在线一区二区 | 未满成年国产在线观看 | 亚洲色www成人永久网址 | 日本护士毛茸茸高潮 | 18禁黄网站男男禁片免费观看 | 大乳丰满人妻中文字幕日本 | 两性色午夜视频免费播放 | 国产精品亚洲五月天高清 | 日韩精品a片一区二区三区妖精 | 国产av无码专区亚洲a∨毛片 | 野外少妇愉情中文字幕 | 久久精品国产精品国产精品污 | 在线观看国产一区二区三区 | 国产精品高潮呻吟av久久4虎 | 中文字幕人妻无码一夲道 | 日韩亚洲欧美精品综合 | 久久久久se色偷偷亚洲精品av | 扒开双腿疯狂进出爽爽爽视频 | 强奷人妻日本中文字幕 | 日韩av无码一区二区三区 | 大肉大捧一进一出视频出来呀 | 久久精品国产日本波多野结衣 | 亚洲国产精品无码久久久久高潮 | 日韩精品无码免费一区二区三区 | 成人精品一区二区三区中文字幕 | 九九在线中文字幕无码 | 天天摸天天碰天天添 | 国内丰满熟女出轨videos | 人妻插b视频一区二区三区 | 成人综合网亚洲伊人 | 日韩 欧美 动漫 国产 制服 | 国产熟妇高潮叫床视频播放 | 娇妻被黑人粗大高潮白浆 | 少女韩国电视剧在线观看完整 | 青青久在线视频免费观看 | 成人无码影片精品久久久 | 99在线 | 亚洲 | 国产乱人无码伦av在线a | 天天拍夜夜添久久精品 | 精品无人区无码乱码毛片国产 | 在线欧美精品一区二区三区 | 中文字幕无码av波多野吉衣 | 无遮挡啪啪摇乳动态图 | 免费无码一区二区三区蜜桃大 | 亚洲国产成人a精品不卡在线 | 国产精品18久久久久久麻辣 | 亚洲自偷自拍另类第1页 | 欧美三级a做爰在线观看 | 婷婷综合久久中文字幕蜜桃三电影 | 午夜肉伦伦影院 | 国产无遮挡又黄又爽免费视频 | 欧美性猛交xxxx富婆 | 久久精品无码一区二区三区 | 日本一区二区更新不卡 | 精品日本一区二区三区在线观看 | 久久综合九色综合97网 | 中文精品无码中文字幕无码专区 |