Java核心技術

回調:一種常見的程序設計模式,在這種設計模式中,可以指出某種特定事件發生時應該採取的動作。(監聽)

1、在指定事件做某事;

2、在指定事件發生時做某事;

對象對於克隆很偏執,如果一個對象請求克隆,但沒有實現這個接口,就會生成一個受查異常。

克隆的時候即使使用clone的默認淺拷貝方法可以實現需求,但是還是需要實現Cloneable接口,將clone重新定義爲public,再調用super.clone()方法。

深拷貝需要克隆對象中可變的實例域,String類型,對象類型。淺拷貝只是指向一個引用。clone不是很常用,標準庫中只有不到5%的類實現了clone。

lambda表達式

背景:1、按指定時間間隔完成工作,需要傳遞一個帶有指定功能的對象。2、按指定規則排序,需要像sort方法傳入一個Comparator對象。

都是將一個代碼塊傳遞到某個對象(一個定時器,或者一個sort方法),這個代碼塊會在將來某個時間被調用。但是在java中傳遞一個代碼塊並不容易,不能直接傳遞代碼塊。java是一個面嚮對象語言,所以必須構造一個對象,這個對象的類需要有一個方法能包含所需的代碼。

lambda表達式是一個可傳遞的代碼塊,可以在以後執行一次或者多次。

對於只有一個抽象方法的接口,需要這種接口的對象時,就可以提供一個lambda表達式。這種接口稱爲函數式接口。

接口,lambda表達式和內部類,lambda表達式的重點是延遲執行。如果想要立即執行的話,完全可以直接執行,而不需使用lambda表達式。使用lambda表達式的原因。

1、在一個單獨的線程中運行代碼

2、多次運行代碼

3、在算法的適當位置運行代碼(eg:排序中的有序操作)

4、發生某種情況時執行的代碼(監聽,定時)

5、只在必要時才運行代碼

函數式接口:僅只有一個函數的接口。

proxy代理

代理:某個事物代替另外一個事物去執行某一個動作的過程。

靜態代理:程序運行的過程代碼由程序員創建或者程序開發工具生成,在編譯期間就已經把目標接口,被代理類,代理類的class文件生成。

動態代理:代理類在程序運行時創建。

用途:利用代理可以在運行時創建一個實現了一組給定接口的新類。這種功能只有在編譯時無法確定需要實現哪個接口時纔有必要使用。

1、何時使用

假設有一個表示接口的Class對象,它的確切類型在編譯時無法知道。要想構造一個實現這些接口的類,就需要使用newInstance方法或反射找出這個類的構造器。但是,不能實例化一個接口,需要在程序處於運行狀態時定義一個新類。

所有的代理類都擴展於Proxy類。一個代理類只有一個實例域——調用處理器,它定義在Proxy的超類中。爲了履行代理對象的職責,所需要的任何附加數據的偶必須存儲在調用處理器中。

克隆和代理是庫設計者和工具構造者感興趣的高級技術,對應用開發來說,他們並不十分重要。

代理類是在程序運行過程中創建。然而,一旦被創建,就變成了常規類,與虛擬機中的任何其他類沒有什麼區別。

assert斷言

斷言可以理解成if,但是如果程序中存在大量的if檢查語句,程序運行起來會非常的慢。斷言機制就是允許在測試期間向代碼中插入一些檢查語句。當代碼發佈時,這些插入的檢測語句將會被自動地移走。簡而言之就是,斷言是會移走的if語句。

問題:在正式發佈的時候移走判斷語句,那麼如果當出現按不常規流程執行代碼的時候,程序是不是會出現異常?既然不會出現其他程序執行情況,爲什麼又要使用斷言呢?是不是會多此一舉?

java的錯誤處理機制:1、異常;2、日誌;3、斷言

att:

1、斷言的失敗是致命的,不可恢復的錯誤;

2、斷言檢查只用於開發和測試階段;

結論:斷言只用於在測試階段確定程序內部的錯誤位置;

T泛型

集合

1、集合類的基本接口是Collection

視圖

可以類比於數據庫中視圖的概念,就是將集合類對象中的數據重新映射到一個數據集合中,但這個集合不是一個物理上存在的對象實體,而是對原集合類對象的再映射,數據物理地址未變,只是訪問數據的接口變了。

java中的視圖,可以說其實就是一個具有限制的集合對象,只不過這裏的不是集合對象,而是一個視圖對象。

例如:這裏有一個Test類

Test[] tests = new Test[10];

List testList = Arrays.asList(tests);這裏返回的對象不是ArrayList它是一個視圖對象,具有訪問底層數組的set和get的方法。但是如果調用改變數組的方法就會拋出異常。所以可以說視圖對象可以說是具有限制的集合對象。

利用java類庫中的方法我們可以獲得不可更改視圖,子視圖等等,這些視圖對於原對象具有不同的操作權限。

併發線程

1、什麼是線程

多任務:在同一時刻運行多個程序的能力。

線程:每一個任務稱爲一個線程,它是線程控制的簡稱。

多線程程序:可以同時運行一個以上線程的程序稱爲多線程程序。

多線程和多進程的區別:一個進程由多個線程組成。每個進程擁有自己的一整套變量,而線程則共享數據。

/**

  • 1、不推薦繼承Thread類,構造子類的對象,然後調用start方法,因爲要將並行運行的任務與運行機制解耦和。

  • 如果有很多任務,要爲每個任務創建一個獨立的線程所付出的代價太大了。

  • 2、不要調用Thread類和Runnable接口的run方法。直接調用run方法,只會執行同一個線程的任務,而不會啓動新線程。

  • 應該調用Thread.start()方法,這個方法將創建一個執行run方法的新線程。

  • Thread(Runnable target) 構造一個新線程,用於調用給定目標的run()方法。

  • void start() 啓動這個線程,將引發調用run()方法。這個方法將立即返回,並且新線程將併發運行。

  • void run() 調用關聯Runnable的run方法。

Thread類使用Runnable接口初始化,Runnable接口中的run方法是一定要重寫覆蓋的。

中斷線程

當線程的run方法執行最後一條語句,並執行return返回時;或者出現未捕獲異常時,線程將終止。(在早期的版本中還有stop函數可以用來終止線程,後來被廢棄)。因此沒有可以強制終止線程的辦法,只有請求中斷的辦法(interrupt方法)。當調用這個方法時,線程的中斷狀態將被置位。每個線程都有該狀態,都會時刻檢查該狀態,以判斷線程是否被中斷。

interrupt,像線程發送中斷請求。中斷狀態被置爲true。如果當前線程被sleep阻塞,那麼拋出InterruptedException異常。

interrupted,檢查當前線程是否被中斷,將當前線程的中斷狀態置爲false。

isInterrupted,檢查線程是否被中斷。

線程被終止的原因:

1、run方法正常退出而自然終止

2、出現了未捕獲的異常而意外終止

線程屬性

1、線程優先級

2、守護線程(守護線程爲其他線程提供服務,eg:計時線程定時發送信號 setDaemon()方法)

3、未捕獲異常處理器

可以用官方提供的setDefaultUncaughtExceptionHandler爲所有線程按照以惡搞默認的處理器。該處理器可以處理未捕獲的異常。

線程組(一個可以統一管理的線程集合。默認情況下,創建的所有線程屬於相同的線程組,但是,也會建立其他的組。現在引入了更好的特性用於線程集合的操作,所以建議不要在自己的程序中使用線程組)

同步

兩個即以上的線程需要共享對同一數據的存取。

線程不安全的原因:線程是併發執行的,即是沒有順序的,有可能兩個線程同時操作同一個數據,也有可能線程在執行的過程中由於某些原因出現中斷,中斷出現的話,會導致其他線程對共享數據造成修改,造成數據的丟失。

鎖對象

防止代碼塊受併發機制的干擾的方法:

1、使用synchronized關鍵字

2、lock()、unlock()

Att:

1、鎖——任何時刻都只能有一個線程執行被保護的代碼。

2、鎖可以管理試圖進入被保護代碼段的線程。

3、鎖可以擁有一個或者多個相關的條件對象。

4、每個條件對象管理那些已經進入了被保護的代碼段但是由於某種原因無法被成功執行的線程。

java中的每一個對象都有一個內部鎖,如果使用synchronized關鍵字來定義方法

public synchronized void method(){} 等價於

public void method(){

this.intrinsiLock.lock();

try{}

finally{this.intrinsiLock.unlock();}

}

由鎖來管理那些試圖進入synchronized方法的進程,由條件對象來管理那些調用wait的線程。如果不滿足某條件,則條件對象.await,之後再條件對象.signalAll();(僅單一條件,等價於wait和notifyAll)

內部鎖的條件存在一些侷限:

1、不能中斷一個正在試圖獲得鎖的線程。
2、試圖獲得鎖時不能設置超時時間。
3、每個鎖僅僅只有單一的條件,當有多個條件限制時是不夠的。

在代碼中應該使用Lock和Condition對象,還是同步方法:

1、最好都不要用,而是使用java.util.concurrent包中的機制;
2、如果synchronized適合則使用;
3、特別需要Lock和Condition對象時才使用;

java獲得鎖的方式:

1、調用synchronized方法
2、進入一個同步阻塞 synchronized(obj) <表明obj對象已經獲取鎖,如果其他對象再獲取,則進入阻塞狀態>

監視器概念

背景:在不需要程序員考慮如何加鎖的情況下,就可以保證多線程的安全性。

Volatile域

將一個域用關鍵字volatile修飾,表明該域可能被另一個線程併發更新。

tryLock()方法用來獲取鎖,獲取的到返回true,否則false。

讀/寫鎖:

ReentrantReadWriteLock成員方法.readLock()返回Lock對象;

ReentrantReadWriteLock成員方法.writeLock()返回Lock對象;

線程中被棄用的函數:

stop(天生就不安全,立刻終止線程的運行,會導致對象處於不一致的狀態);

suspend(經常導致死鎖,線程被掛起之前已經獲得鎖,那麼會導致另外的線程無法獲得該鎖導致死鎖);

resume(),suspend已被棄用,沒必要使用resume 將一個掛起的線程復活後繼續執行。

阻塞隊列

可以理解成緩衝區的形式,使用一個或者多個隊列的形式來優雅安全的將其形式化,生產者向隊列中插入元素,然後消費者取出。對象由java.util.concurrent包提供。

LinkedBlockingDeque、LinkedBlockingQueue

對應集合list、map等,也有線程安全的集合,由java.util.concurrent提供,比如ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue

對併發散列映射的批操作

在併發場景下使用的map數據集合

其實這部分如果有需要直接看api就好,書中寫的雜亂無章沒頭沒尾的,花了時間看也看不懂。

併發集視圖

如果想要一個大的線程安全的集而不是映射。並沒有ConcurrentHashSet類,可以使用ConcurrentHashMap<K,Boolean>,用key序列開表示集。也就是將map的value值部分置爲無意義的,直接使用key來存儲數據,相當於map的一種變形吧,借鑑思想。

Callable

Runnable封裝一個異步運行的任務,可以將其視爲一個沒有參數和返回值的異步方法。Callable和Runnable類似,但是有返回值。Callable接口是一個參數化的類型,只有一個call方法。

//表示一個最終返回Integer對象的異步計算

public interface Callable<V>{
    V call() throws Exception;
}

Future接口

//保存異步計算的結果。可以啓動一個Callable,重寫call方法,用callable初始化Future對象,將future對象交給某個線程,運行線程,調用future的isDone()方法來判斷計算是否完成。

FutureTask<Chuju> task = new FutureTask<>(Callable<Chuju>);
public interface Future<V>{
}

 
public static void main(String[] args) throws InterruptedException, ExecutionException {
    long startTime = System.currentTimeMillis();

    // 第一步 網購廚具
    Callable<Chuju> onlineShopping = ()->{
            System.out.println("第一步:下單");
            System.out.println("第一步:等待送貨");
            Thread.sleep(5000);  // 模擬送貨時間
            System.out.println("第一步:快遞送到");
            return new Chuju();
    };
    FutureTask<Chuju> task = new FutureTask<>(onlineShopping);
    new Thread(task).start();
    // 第二步 去超市購買食材
    Thread.sleep(2000);  // 模擬購買食材時間
    Shicai shicai = new Shicai();
    System.out.println("第二步:食材到位");
    // 第三步 用廚具烹飪食材
    if (!task.isDone()) {  // 聯繫快遞員,詢問是否到貨
        System.out.println("第三步:廚具還沒到,心情好就等着(心情不好就調用cancel方法取消訂單)");
    }
    Chuju chuju = task.get();
    System.out.println("第三步:廚具到位,開始展現廚藝");
    cook(chuju, shicai);

    System.out.println("總共用時" + (System.currentTimeMillis() - startTime) + "ms");
}

FutureTask包裝器可以同時將Callable轉換成Future和Runnable,它同時實現二者的接口。

執行器

線程池中包含了許多準備運行的空閒線程,這些線程可以接受Runnable方法,然後運行,運行之後不結束,繼續留着接受其他的Runnable方法。

構建線程需要代價,因爲設計到與操作系統的交互,創建大量線程會降低性能甚至使jvm崩潰。

執行器Executor類有許多靜態工廠方法用來構建線程池。
線程池目前使用類:java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類。

1、Executors執行器、ThreadPoolExecutor線程池。ExecutorService pool = Executors.newCachedThreadPool();
2、ScheduledExecutorService預定執行,是一種允許線程池機制的java.util.Timer的泛化。
3、控制任務組 --> eg:在執行器中使用shutdownNow方法取消所有的任務。
4、Fork-Join框架,解決應用可能對每個處理器內核分別使用一個線程,來完成計算密集型任務,如圖像或視頻處理。

同步器

1、信號量(類似PV操作信號量)

2、倒計時門栓(讓線程集等待直到計數變爲0。倒計時門栓是一次性的,一旦計數爲0,就不能重用了。計數值爲1,線程在門外等候直到另外一個線程將計數器值置爲0)

3、障柵(考慮大量線程都運行‘一段代碼‘的不同部分的情形,結果是所有結果的組合,當一個線程完成了自己的部分時,就讓它運行到障柵處,一旦所有線程都執行完成到達障柵,就撤銷障柵,使程序繼續向下執行)

4、交換器(當兩個線程在同一個數據緩衝區的兩個實例上工作的時候。比如一個線程向緩衝區填入數據,一個線程消耗這些數據。當它們都完成以後,相互交換緩衝區)

5、同步隊列(將生產者和消費者線程配對的機制,當一個線程調用SynchronousQueue的put方法時,它會阻塞直到另一個線程調用take方法爲止)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章