java平常知識點積累


前言java

  • 知識點整理來源於網絡,我的只是單純備份記錄,若有侵權請聯繫本人處理(lvzhi1988@126.com),持續整理中。。。

1,java多線程與synchronized

  • java類型中的普通非static方法

示例代碼:mysql

package com.lvzhi;

/** * Created by lvzhi on 2017/9/3 */
public class MyThread {
    private int num = 0;

    public synchronized void print(String args) throws InterruptedException {
        if (args.equals("a")) {
            System.out.println("I'am a ");
            num = 100;
            Thread.sleep(1000L);
        } else {
            System.out.println("I'am b ");
            num = 200;
            Thread.sleep(1000L);
        }
        System.out.println("Over");
    }

    public static void main(String[] args) {
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    myThread1.print("a");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    myThread2.print("b");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread1.start();
        thread2.start();

    }
}

複製代碼

該段代碼輸出的結果是thread1和thread2的亂序結果,由於synchronized是在非static方法,那麼對於線程來講就是myThread1和myThread2是相互獨立的,相似於上邊的代碼跟沒加synchronized效果是同樣的。 結果以下: I'am a I'am b Over Oversql


*java類型中static方法編程

示例代碼:數組

package com.lvzhi;

/** * Created by lvzhi on 2017/9/3 */
public class MyThread {
    private static int num = 0;

    public static synchronized void print(String args) throws InterruptedException {
        if (args.equals("a")) {
            System.out.println("I'am a ");
            num = 100;
            Thread.sleep(1000L);
        } else {
            System.out.println("I'am b ");
            num = 200;
            Thread.sleep(1000L);
        }
        System.out.println("Over");
    }

    public static void main(String[] args) {
        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    myThread1.print("a");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    myThread2.print("b");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread1.start();
        thread2.start();

    }
}

複製代碼

該段代碼輸出的結果是thread1和thread2的有序結果,由於synchronized是在static方法上,static方法特性本身去百度,因此結果是有序的。 結果以下: I'am a Over I'am b Over瀏覽器

我的總結:synchronized加在static鎖定的就是class類,類級別的鎖。服務器


2,java同一個類中不一樣方法的synchronized

  • 結論:同一個類中的全部synchronized方法,若是有一個synchronized方法已經被使用,那麼該類中其餘全部的synchronized方法都不能被使用,只能等第一個synchronized方法被釋放,其餘的synchronized方法才能使用;可是,其餘的非synchronized方法能夠被調用。結果就是synchronized鎖定的class對象,第一個線程調用某個synchronized方法時,該class就被鎖定了。 結合1,若是又new出來一個該類,那麼是不衝突的。

3,髒讀

  • 在oracle中,有一個概念叫「undo」,就是每次修改或者刪除一條數據的時候,會把這個數據放置在undo中,若是須要回滾就去undo中找。 因此,若是在9點這一刻發起一個查詢請求,檢索一條數據,這個請求須要執行10分鐘才能找到想要的數據,可是在9點5分的時候,有另個請求修改了想要查詢的數據,那麼在9點10分的時候,查看到的仍是老數據,就是由於「undo」。 在mysql中應該也是同樣的。查詢發出的那一刻,數據是什麼就是什麼。

4, 類鎖,對象鎖,方法鎖

  • 1 ,其實只有兩種鎖,方法鎖和對象鎖是同一個東西,因此只有對象鎖和類鎖。
  • 2,synchronized 加到 static 方法前面是給class 加鎖,即類鎖;而synchronized 加到非靜態方法前面是給對象上鎖。
  • 3, 若是多線程同時訪問同一類的 類鎖(synchronized 修飾的靜態方法)以及對象鎖(synchronized 修飾的非靜態方法)這兩個方法執行是異步的,緣由:類鎖和對象鎖是2中不一樣的鎖。
  • 4, 類鎖對該類的全部對象都能起做用,而對象鎖不能。

5,sleep & wait | notify | notifyAll

1、sleep & wait網絡

  1. 二者來自不一樣的類(分別是Thread和Object) 2.sleep方法沒有釋放鎖,而wait方法釋放了鎖,使得其餘線程可使用同步控制塊或方法 3.wiat只能在同步控制方法或者同步控制塊使用,而sleep能夠在任何地方使用 4.sleep必須捕獲異常,而wait不須要 2、wait和notify爲何會封裝在Object類中,而不是像sleep方法在Thread中? wait和notify的本質是基於條件對象,並且只能由已經得到鎖的線程調用。java的每一個Object都有一個隱式鎖,這個隱式鎖關聯一個Condition條件對象, 線程拿到這個隱式所(好比進入synchronized代碼區域),就能夠調用wait,語義是Condition條件對象上等待,其餘的線程能夠在這個Condition條件對象上等待 , 等知足條件以後,就能夠調用notify或者notifyAll來喚醒全部在此條件對象上等待的線程。 3、死鎖(產生死鎖的必要條件) 互斥條件:一個資源每次只能被一個進程使用 請求與保持條件:一個進程因請求資源而阻塞時,對已得到的資源保持不放。 不剝奪條件:進程已得到的資源,在未使用完以前,不能強制剝奪 循環等待條件:若干進程之間造成一種頭尾相接的循環資源關係。 這四個條件時死鎖的必要條件,只有系統發生死鎖,這些條件必然成立,只要上述條件之一不知足,就不發生死鎖。 4、常識 1.在java中全部線程都是同時啓動的,至於何時,哪一個先執行,徹底看誰先獲得CPU的資源。 2.在java中,每次程序運行至少啓動2個線程。一個是main線程,一個是垃圾回收線程。由於每當使用java命令執行一個類的時候,實際上啓動了一個jvm, 每一個jvm其實就是在操做系統中啓動了一個線程 3.理解java編譯器的線程處理和jvm。有助於編程高效、性能更好的java代碼。 5、java中wait/notify機制 notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM肯定喚醒哪一個線程, 並且不是按優先級。notify()方法和wait()方法的基本思想是給方法或代碼塊提供一種相互通訊的方式,而這些方法或者代碼塊同步於某個特定對象。 代碼塊能夠調用wait()方法來將自身的操做掛起,直到同一個對象上的其餘同步方法或同步代碼塊以某種方式將其改變,並調用notfiy()方法來通知此代碼塊改變已經完成。 一個線程通常會由於它所同步的對象的某個屬性沒有設置,或者某個條件沒有知足而調用wait()方法,這些由另外一個線程的動做決定。 最簡單的狀況多是資源由於正被另外一個線程修改而繁忙,還有其餘的可能狀況。 通 常,多線程之間須要協調工做。例如,瀏覽器的一個顯示圖片的線程displayThread想要執行顯示圖片的任務,必須等待下載線 程 downloadThread將該圖片下載完畢。若是圖片尚未下載完,displayThread能夠暫停,當downloadThread完成了 任務 後,再通知displayThread「圖片準備完畢,能夠顯示了」,這時,displayThread繼續執行。 以上邏輯簡單的說就是:若是條件不知足,則等待。當條件知足時,等待該條件的線程將被喚醒。在Java中,這個機制的實現依賴於wait/notify。等待機制與鎖機制是密切關聯的。例如: synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();} 當線程A得到了obj鎖後,發現條件condition不知足,沒法繼續下一處理,因而線程A就wait()。 在另外一線程B中,若是B更改了某些條件,使得線程A的condition條件知足了,就能夠喚醒線程A: synchronized(obj) {condition = true;obj.notify();} 須要注意的概念是: ◆調用obj的wait(), notify()方法前,必須得到obj鎖,也就是必須寫在synchronized(obj) {...} 代碼段內。 ◆調用obj.wait()後,線程A就釋放了obj的鎖,不然線程B沒法得到obj鎖,也就沒法在synchronized(obj) {...} 代碼段內喚醒A。 ◆當obj.wait()方法返回後,線程A須要再次得到obj鎖,才能繼續執行。 ◆若是A1,A2,A3都在obj.wait(),則B調用obj.notify()只能喚醒A1,A2,A3中的一個(具體哪個由JVM決定)。 ◆obj.notifyAll()則能所有喚醒A1,A2,A3,可是要繼續執行obj.wait()的下一條語句,必須得到obj鎖,所以,A1,A2,A3只有一個有機會得到鎖繼續執行,例如A1,其他的須要等待A1釋放obj鎖以後才能繼續執行。 ◆當B調用obj.notify/notifyAll的時候,B正持有obj鎖,所以,A1,A2,A3雖被喚醒,可是仍沒法得到obj鎖。直到B退出synchronized塊,釋放obj鎖後,A1,A2,A3中的一個纔有機會得到鎖繼續執行。 7、join join方法的功能就是使異步執行的線程變成同步執行。也就是說,當調用線程實例的start方法後,這個方法會當即返回,若是在調用start方法後後須要使用一個由這個線程計算獲得的值,就必須使用join方法。若是不使用join方法,就不能保證當執行到start方法後面的某條語句時,這個線程必定會執行完。而使用join方法後,直到這個線程退出,程序纔會往下執行。例如:你準備洗澡,須要準備的步驟,準備好衣服,沐浴的東西及燒水這些事情,因爲燒水耗時太長,若是也放在主線程之中,就很浪費資源,因此若是咱們另開線程去處 理,就會達到很好效果,因而乎在準備好衣服,沐浴的東西以前就去開子線程燒水,燒水的過程當中主線程準備好衣服,沐浴的東西,此時就等待水燒好,而後方可痛 快的洗澡了!!

6,ConcurrentHashMap

有時間讀讀源碼session

7,Queue

  • 隊列是一種數據結構.它有兩個基本操做:在隊列尾部加人一個元素,和從隊列頭部移除一個元素就是說,隊列以一種先進先出的方式管理數據,若是你試圖向一個 已經滿了的阻塞隊列中添加一個元素或者是從一個空的阻塞隊列中移除一個元索,將致使線程阻塞.在多線程進行合做時,阻塞隊列是頗有用的工具。
  • add 增長一個元索 若是隊列已滿,則拋出一個IIIegaISlabEepeplian異常 remove 移除並返回隊列頭部的元素 若是隊列爲空,則拋出一個NoSuchElementException異常 element 返回隊列頭部的元素 若是隊列爲空,則拋出一個NoSuchElementException異常 offer 添加一個元素並返回true 若是隊列已滿,則返回false poll 移除並返問隊列頭部的元素 若是隊列爲空,則返回null peek 返回隊列頭部的元素 若是隊列爲空,則返回null put 添加一個元素 若是隊列滿,則阻塞 take 移除並返回隊列頭部的元素 若是隊列爲空,則阻塞

8,CopyOnWriteArrayList

  • 讀的時候不須要加鎖,若是讀的時候有多個線程正在向ArrayList添加數據,讀仍是會讀到舊的數據,由於寫的時候不會鎖住舊的ArrayList。
  • CopyOnWrite併發容器用於讀多寫少的併發場景。好比白名單,黑名單,商品類目的訪問和更新場景
  • CopyOnWrite的缺點 CopyOnWrite容器有不少優勢,可是同時也存在兩個問題,即內存佔用問題和數據一致性問題。因此在開發的時候須要注意一下。 內存佔用問題: 由於CopyOnWrite的寫時複製機制,因此在進行寫操做的時候,內存裏會同時駐紮兩個對象的內存,舊的對象和新寫入的對象(注意:在複製的時候只是複製容器裏的引用,只是在寫的時候會建立新對象添加到新容器裏,而舊容器的對象還在使用,因此有兩份對象內存)。若是這些對象佔用的內存比較大,好比說200M左右,那麼再寫入100M數據進去,內存就會佔用300M,那麼這個時候頗有可能形成頻繁的Yong GC和Full GC。以前咱們系統中使用了一個服務因爲每晚使用CopyOnWrite機制更新大對象,形成了每晚15秒的Full GC,應用響應時間也隨之變長。 針對內存佔用問題,能夠經過壓縮容器中的元素的方法來減小大對象的內存消耗,好比,若是元素全是10進制的數字,能夠考慮把它壓縮成36進制或64進制。或者不使用CopyOnWrite容器,而使用其餘的併發容器,如ConcurrentHashMap。 數據一致性問題: CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。因此若是你但願寫入的的數據,立刻能讀到,請不要使用CopyOnWrite容器。

9,阻塞和非阻塞隊列

  • 阻塞隊列與普通隊列的區別在於,當隊列是空的時,從隊列中獲取元素的操做將會被阻塞,或者當隊列是滿時,往隊列裏添加元素的操做會被阻塞。試圖從空的阻塞隊列中獲取元素的線程將會被阻塞,直到其餘的線程往空的隊列插入新的元素。一樣,試圖往已滿的阻塞隊列中添加新元素的線程一樣也會被阻塞,直到其餘的線程使隊列從新變得空閒起來,如從隊列中移除一個或者多個元素,或者徹底清空隊列.
  • 1.ArrayDeque, (數組雙端隊列) 2.PriorityQueue, (優先級隊列) 3.ConcurrentLinkedQueue, (基於鏈表的併發隊列) 4.DelayQueue, (延期阻塞隊列)(阻塞隊列實現了BlockingQueue接口) 5.ArrayBlockingQueue, (基於數組的併發阻塞隊列) 6.LinkedBlockingQueue, (基於鏈表的FIFO阻塞隊列) 7.LinkedBlockingDeque, (基於鏈表的FIFO雙端阻塞隊列) 8.PriorityBlockingQueue, (帶優先級的無界阻塞隊列) 9.SynchronousQueue (併發同步阻塞隊列)
  • ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。 LinkedBlockingQueue :一個由鏈表結構組成的有界阻塞隊列。 PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。 DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。 SynchronousQueue:一個不存儲元素的阻塞隊列。 LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。 LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。
  • 方法\處理方式 拋出異常 返回特殊值 一直阻塞 超時退出 插入方法 add(e) offer(e) put(e) offer(e,time,unit) 移除方法 remove() poll() take() poll(time,unit) 檢查方法 element() peek() 不可用 不可用
  • 拋出異常:是指當阻塞隊列滿時候,再往隊列裏插入元素,會拋出IllegalStateException(「Queue full」)異常。當隊列爲空時,從隊列裏獲取元素時會拋出NoSuchElementException異常 。 返回特殊值:插入方法會返回是否成功,成功則返回true。移除方法,則是從隊列裏拿出一個元素,若是沒有則返回null 一直阻塞:當阻塞隊列滿時,若是生產者線程往隊列裏put元素,隊列會一直阻塞生產者線程,直到拿到數據,或者響應中斷退出。當隊列空時,消費者線程試圖從隊列裏take元素,隊列也會阻塞消費者線程,直到隊列可用。 超時退出:當阻塞隊列滿時,隊列會阻塞生產者線程一段時間,若是超過必定的時間,生產者線程就會退出。 BlockingQueue
  • 獲取元素的時候等待隊列裏有元素,不然阻塞 保存元素的時候等待隊列裏有空間,不然阻塞 用來簡化生產者消費者在多線程環境下的開發
  • ArrayBlockingQueue FIFO、數組實現 有界阻塞隊列,一旦指定了隊列的長度,則隊列的大小不能被改變 在生產者消費者例子中,若是生產者生產實體放入隊列超過了隊列的長度,則在offer(或者put,add)的時候會被阻塞,直到隊列的實體數量< 隊列的初始size爲止。不過能夠設置超時時間,超時後隊列還未空出位置,則offer失敗。 若是消費者發現隊列裏沒有可被消費的實體時也會被阻塞,直到有實體被生產出來放入隊列位置,不過能夠設置等待的超時時間,超過期間後會返回null
  • DelayQueue 有界阻塞延時隊列,當隊列裏的元素延時期未到是,經過take方法不能獲取,會被阻塞,直到有元素延時到期爲止 如: 1.obj 5s 延時到期 2.obj 6s 延時到期 3.obj 9s 延時到期 那麼在take的時候,須要等待5秒鐘才能獲取第一個obj,再過1s後能夠獲取第二個obj,再過3s後能夠得到第三個obj 這個隊列能夠用來處理session過時失效的場景,好比session在建立的時候設置延時到期時間爲30分鐘,放入延時隊列裏,而後經過一個線程來獲取這個隊列元素,只要能被獲取到的,表示已是過時的session,被獲取的session能夠確定超過30分鐘了,這時對session進行失效。
  • LinkedBlockingQueue FIFO、Node鏈表結構 能夠經過構造方法設置capacity來使得阻塞隊列是有界的,也能夠不設置,則爲無界隊列 其餘功能相似ArrayBlockingQueue
  • PriorityBlockingQueue 無界限隊列,至關於PriorityQueue + BlockingQueue 插入的對象必須是可比較的,或者經過構造方法實現插入對象的比較器Comparator<? super E> 隊列裏的元素按Comparator<? super E> comparator比較結果排序,PriorityBlockingQueue能夠用來處理一些有優先級的事物。好比短信發送優先級隊列,隊列裏已經有某企業的100000條短信,這時候又來了一個100條緊急短信,優先級別比較高,能夠經過PriorityBlockingQueue來輕鬆實現這樣的功能。這樣這個100條能夠被優先發送
  • SynchronousQueue 無內部容量的阻塞隊列,put必須等待take,一樣take必須等待put。比較適合兩個線程間的數據傳遞。異步轉同步的場景不太適用,由於對於異步線程來講在處理完事務後進行put,可是必須等待put的值被取走。

10,Future

這個配合callable使用,其實就是多線程有返回值,so easy !數據結構

11,master-worker

Master-Worker是經常使用的並行計算模式。它的核心思想是系統由兩類進程協做工做:Master進程和Worker進程。Master負責接收和分配任務,Worker負責處理子任務。當各個Worker子進程處理完成後,會將結果返回給Master,由Master做概括總結。其好處就是能將一個大任務分解成若干個小任務,並行執行,從而提升系統的吞吐量。處理過程以下圖所示:

master-worker工做模式圖
Master-Worker模式是一種將串行任務並行化的方案,被分解的子任務在系統中能夠被並行處理,同時,若是有須要,Master進程不須要等待全部子任務都完成計算,就能夠根據已有的部分結果集計算最終結果集。

12,Semaphore信號量

Semaphore當前在多線程環境下被擴放使用,操做系統的信號量是個很重要的概念,在進程控制方面都有應用。Java 併發庫 的Semaphore 能夠很輕鬆完成信號量控制,Semaphore能夠控制某個資源可被同時訪問的個數,經過 acquire() 獲取一個許可,若是沒有就等待,而 release() 釋放一個許可。好比在Windows下能夠設置共享文件的最大客戶端訪問個數。 Semaphore實現的功能就相似廁全部5個坑,假若有10我的要上廁所,那麼同時只能有多少我的去上廁所呢?同時只能有5我的可以佔用,當5我的中 的任何一我的讓開後,其中等待的另外5我的中又有一我的能夠佔用了。另外等待的5我的中能夠是隨機得到優先機會,也能夠是按照先來後到的順序得到機會,這取決於構造Semaphore對象時傳入的參數選項。單個信號量的Semaphore對象能夠實現互斥鎖的功能,而且能夠是由一個線程得到了「鎖」,再由另外一個線程釋放「鎖」,這可應用於死鎖恢復的一些場合。 ps:有個重要問題,該段代碼在main方法中運行,能夠達到預期的效果,若是用junit的方法來測試,其預期的結果差很遠。

public class SemaphoreTest {
    public static void main(String[] args) {
        // 線程池
        ExecutorService exec = Executors.newCachedThreadPool();
        // 只能5個線程同時訪問
        final Semaphore semp = new Semaphore(5);
        // 模擬20個客戶端訪問
        for (int index = 0; index < 50; index++) {
            final int NO = index;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        // 獲取許可
                        semp.acquire();
                        System.out.println("Accessing: " + NO);
                        Thread.sleep((long) (Math.random() * 10000));
                        // 訪問完後,釋放
                        semp.release();
                        //availablePermits()指的是當前信號燈庫中有多少個能夠被使用
                        System.out.println("-----------------" + semp.availablePermits());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            exec.execute(run);
        }
        // 退出線程池
        exec.shutdown();
    }
複製代碼

13,condition

  1. Condition能夠替代傳統的線程間通訊,用await()替代wait,用signal替代notify(),用signalAll()替代notifyAll()。由於Object下面的wait/notify/notifyAll方法都是final的,因此名稱上全都發生了改變。傳統線程通訊方式,condition都能實現。
  2. 注意:condition()是被綁定到Lock上面的,要建立一個Lock的conditon,須要用newCondition 。如今知道了,synchronized和notidy/wait/notifyAll結合使用, lock和condition的await/signal/signalAll結合使用。
  3. condition的強大之處,它能夠爲多個線程之間建立不一樣的condition。
public class ConditionTest {
    private static Lock lock = new ReentrantLock();
    private static final Condition firstCondition = lock.newCondition();
    private static final  Condition secondCondition = lock.newCondition();
    //以上的兩個condition是不同的,不能用secondCondition去喚醒firstCondition
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    System.out.println("wait");
                    firstCondition.await();
                    System.out.println("over");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    System.out.println("enter");
                    Thread.sleep(10000);
                    System.out.println("out");
                    firstCondition.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    lock.unlock();
                }
            }
        }).start();
    }
}
複製代碼

14,ReentrantLock(重入鎖)

公平鎖:公平鎖講究先來先到,線程在獲取鎖時,若是這個鎖的等待隊列中已經有線程在等待,那麼當前線程就會進入等待隊列中; 非公平鎖:無論是否有等待隊列,若是能夠獲取鎖,則馬上佔有鎖對象。 一、ReentrantLock 擁有Synchronized相同的併發性和內存語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候

線程A和B都要獲取對象O的鎖定,假設A獲取了對象O鎖,B將等待A釋放對O的鎖定,

 若是使用 synchronized ,若是A不釋放,B將一直等下去,不能被中斷

 若是 使用ReentrantLock,若是A不釋放,可使B在等待了足夠長的時間之後,中斷等待,而幹別的事情
複製代碼
  • ReentrantLock獲取鎖定與三種方式: a) lock(), 若是獲取了鎖當即返回,若是別的線程持有鎖,當前線程則一直處於休眠狀態,直到獲取鎖 b) tryLock(), 若是獲取了鎖當即返回true,若是別的線程正持有鎖,當即返回false; c)tryLock(long timeout,TimeUnit unit), 若是獲取了鎖定當即返回true,若是別的線程正持有鎖,會等待參數給定的時間,在等待的過程當中,若是獲取了鎖定,就返回true,若是等待超時,返回false; d) lockInterruptibly:若是獲取了鎖定當即返回,若是沒有獲取鎖定,當前線程處於休眠狀態,直到或者鎖定,或者當前線程被別的線程中斷 二、synchronized是在JVM層面上實現的,不但能夠經過一些監控工具監控synchronized的鎖定,並且在代碼執行時出現異常,JVM會自動釋放鎖定,可是使用Lock則不行,lock是經過代碼實現的,要保證鎖定必定會被釋放,就必須將unLock()放到finally{}中 三、在資源競爭不是很激烈的狀況下,Synchronized的性能要優於ReetrantLock,可是在資源競爭很激烈的狀況下,Synchronized的性能會降低幾十倍,可是ReetrantLock的性能能維持常態; 目前來講,就是lock和unlock,其它的也不知道多少,等到再找時間再具體研究。

15 ReentrantReadWriteLock(讀寫鎖)

  • 讀寫鎖的機制: "讀-讀"不互斥 "讀-寫"互斥 "寫-寫"互斥
  • 即在任什麼時候候必須保證: 只有一個線程在寫入; 線程正在讀取的時候,寫入操做等待; 線程正在寫入的時候,其餘線程的寫入操做和讀取操做都要等待;
  • java.util.concurrent.locks包定義了兩個鎖類,ReentrantLock和ReentrantReadWriteLock類。 當有不少線程都從某個數據結構中讀取數據而不多有線程對其進行修改時,後者就頗有用了。在這種狀況下,容許讀取器線程共享訪問是合適的。固然,寫入器線程依然必須是互斥訪問的 下面是使用讀/寫鎖的必要步驟: (1) 建立一個ReentrantReadWriteLock對象 [java] view plain copy private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    (2)抽取讀鎖和寫鎖: [java] view plain copy private Lock readLock = rwl.readLock();//獲得一個可被多個讀操做共用的讀鎖,但它會排斥全部寫操做
    private Lock writeLock = rwl.writeLock();//獲得一個寫鎖,它會排斥全部其餘的讀操做和寫操做
    (3) 對全部訪問者加讀鎖 [java] view plain copy public double getTotalBalance(){
    readLock.lock();
    try{...};
    finally{readLock.unlock();}
    } 對全部修改者加寫鎖 [java] view plain copy public void transfer(){
    writeLock.lock();
    try{...};
    finally{writeLock.unlock();}
    }

16, CountDownLatch、CyclicBarrier和Semaphore

其實,這三個均可以用於多線程中,好久之前就用過,只不過又忘記了。 其實就是,能夠等待全部的線程都執行完,能夠彙總一下。再此,再記錄一下。

17,Disruptor

暫時不研究,目前全部的項目都沒用過,用過的框架中不知道用沒有用,因此,僅此先記錄一下,若是之後工做中用到,再仔細研究。

18, 同步、異步、阻塞、非阻塞

  • 1,同步和異步是針對應用程序與內核的交互而言的。

  • 2,阻塞和非阻塞是針對於進程在訪問數據的時候,根據IO操做的就緒狀態來採起的不一樣方式,說白了是一種讀取或者寫入操做函數的實現方式,阻塞方式下讀取或者寫入函數將一直等待,而非阻塞方式下,讀取或者寫入函數會當即返回一個狀態值。

  • 由上描述基本能夠總結一句簡短的話,同步和異步是目的,阻塞和非阻塞是實現方式。


  • 1,同步: 指的是用戶進程觸發IO操做並等待或者輪詢的去查看IO操做是否就緒。
  • 2,異步: 異步是指用戶進程觸發IO操做之後便開始作本身的事情,而當IO操做已經完成的時候會獲得IO完成的通知(異步的特色就是通知) 告訴朋友本身合適衣服的尺寸,大小,顏色,讓朋友委託去賣,而後本身能夠去幹別的事。(使用異步IO時,Java將IO讀寫委託給OS處理,須要將數據緩衝區地址和大小傳給OS)
  • 3,阻塞: 所謂阻塞方式的意思是指, 當試圖對該文件描述符進行讀寫時, 若是當時沒有東西可讀,或者暫時不可寫, 程序就進入等待 狀態, 直到有東西可讀或者可寫爲止 去公交站充值,發現這個時候,充值員不在(可能上廁所去了),而後咱們就在這裏等待,一直等到充值員回來爲止。(固然現實社會,可不是這樣,可是在計算機裏確實如此。)
  • 4,非阻塞: 非阻塞狀態下, 若是沒有東西可讀, 或者不可寫, 讀寫函數立刻返回,而不會等待, 銀行裏取款辦業務時,領取一張小票,領取完後咱們本身能夠玩玩手機,或者與別人聊聊天,當輪咱們時,銀行的喇叭會通知,這時候咱們就能夠去了。

  • 同步阻塞IO(JAVA BIO): 同步並阻塞,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。

  • 同步非阻塞IO(Java NIO) : 同步非阻塞,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。用戶進程也須要時不時的詢問IO操做是否就緒,這就要求用戶進程不停的去詢問。

  • 異步阻塞IO(Java NIO):
    此種方式下是指應用發起一個IO操做之後,不等待內核IO操做的完成,等內核完成IO操做之後會通知應用程序,這其實就是同步和異步最關鍵的區別,同步必須等待或者主動的去詢問IO是否完成,那麼爲何說是阻塞的呢?由於此時是經過select系統調用來完成的,而select函數自己的實現方式是阻塞的,而採用select函數有個好處就是它能夠同時監聽多個文件句柄(若是從UNP的角度看,select屬於同步操做。由於select以後,進程還須要讀寫數據),從而提升系統的併發性!

  • (Java AIO(NIO.2))異步非阻塞IO:
    在此種模式下,用戶進程只須要發起一個IO操做而後當即返回,等IO操做真正的完成之後,應用程序會獲得IO操做完成的通知,此時用戶進程只須要對數據進行處理就行了,不須要進行實際的IO讀寫操做,由於真正的IO讀取或者寫入操做已經由內核完成了。

同步和異步是相對於應用和內核的交互方式而言的,同步 須要主動去詢問,而異步的時候內核在IO事件發生的時候通知應用程序,而阻塞和非阻塞僅僅是系統在調用系統調用的時候函數的實現方式而已。

  • For Example: 若是你想吃一份宮保雞丁蓋飯: 同步阻塞:你到飯館點餐,而後在那等着,還要一邊喊:好了沒啊! 同步非阻塞:在飯館點完餐,就去遛狗了。不過溜一下子,就回飯館喊一聲:好了沒啊! 異步阻塞:遛狗的時候,接到飯館電話,說飯作好了,讓您親自去拿。 異步非阻塞:飯館打電話說,咱們知道您的位置,一會給你送過來,安心遛狗就能夠了。

19,AIO

目前理解起來有點沒有徹底搞明白,等之後再搞一次。

20,&、|、^、<<、>>>

A = 0011 1100 B = 0000 1101

操做符 描述 例子
若是相對應位都是1,則結果爲1,不然爲0 (A&B),獲得12,即0000 1100
| 若是相對應位都是0,則結果爲0,不然爲1 (A | B)獲得61,即 0011 1101
^ 若是相對應位值相同,則結果爲0,不然爲1 (A ^ B)獲得49,即 0011 0001
按位補運算符翻轉操做數的每一位,即0變成1,1變成0。 (〜A)獲得-61,即1100 0011
<<  按位左移運算符。左操做數按位左移右操做數指定的位數。 A << 2獲得240,即 1111 0000
>>  按位右移運算符。左操做數按位右移右操做數指定的位數。 A >> 2獲得15即 1111
>>>  按位右移補零操做符。左操做數的值按右操做數指定的位數右移,移動獲得的空位以零填充。 A>>>2獲得15即0000 1111
> >* 負數的表示方法: 原碼:一個整數,按照絕對值大小轉換成的二進制數,稱爲原碼。 反碼:將二進制數按位取反,所得的新二進制數稱爲原二進制數的反碼。 補碼:反碼加1稱爲補碼。 ![此處輸入圖片的描述][2]

21,java中byte轉換int時與0xff進行運算的緣由

byte只有8位,若是byte中的數字爲負數,那麼在默認轉化位int的時候會發生錯誤,0XFF默認是整形,運算後,就至關於顯式的轉換爲int,不會出現二進制上的錯誤。

⑴一個數爲正,則它的原碼、反碼、補碼相同 ⑵一個數爲負,剛符號位爲1,其他各位是對原碼取反,而後整個數加1

-1的原碼爲 10000001 -1的反碼爲 11111110

-1的補碼爲 11111111

0的原碼爲 00000000 0的反碼爲 11111111(正零和負零的反碼相同)

0的補碼爲 100000000(舍掉打頭的1,正零和負零的補碼相同)

Integer.toHexString的參數是int,若是不進行&0xff,那麼當一個byte會轉換成int時,因爲int是32位,而byte只有8位這時會進行補位, 例如補碼11111111的十進制數爲-1轉換爲int時變爲11111111111111111111111111111111好多1啊,呵呵!即0xffffffff可是這個數是不對的,這種補位就會形成偏差。 和0xff相與後,高24比特就會被清0了,結果就對了。 Java中的一個byte,其範圍是-128~127的,而Integer.toHexString的參數原本是int,若是不進行&0xff,那麼當一個byte會轉換成int時,對於負數,會作位擴展,舉例來講,一個byte的-1(即0xff),會被轉換成int的-1(即0xffffffff),那麼轉化出的結果就不是咱們想要的了。 而0xff默認是整形,因此,一個byte跟0xff相與會先將那個byte轉化成整形運算,這樣,結果中的高的24個比特就總會被清0,因而結果老是咱們想要的

22,clone

  • shadow clone(淺克隆):一般只是對克隆的實例進行復制,但裏面的其餘子對象,都是共用的。【簡單粗暴的理解:被克隆對象的基本數據類型屬性都拷貝了,可是被克隆對象的對象屬性,只是拷貝了個引用,該引用仍是指向了堆中原來對象。一句話,若是克隆對象修改基本類型屬性,那麼被克隆對象不會被修改;若是克隆對象修改對象屬性,那麼被克隆對象就被修改了】
  • deep clone(深克隆): 克隆的時候會複製它的子對象的引用,裏面全部的變量和子對象都是又額外拷貝了一份。【簡單粗暴的理解:被克隆對象徹底被複制了一份,克隆出來的對象不管修改什麼屬性,被克隆對象都不會變更】
  • 代碼示例
package com.lvzhi.clone;

import java.io.Serializable;

/** * Created by lvzhi on 2018/1/14 */
public class Wife implements Serializable {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Wife{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

複製代碼
package com.lvzhi.clone;

import java.io.*;

/** * Created by lvzhi on 2018/1/14 */
public class Husband implements Cloneable, Serializable {
    private String name;
    private int age;
    private Wife wife;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Husband husband = null;
        try{
            husband = (Husband)super.clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }finally{
            return husband;
        }
    }

    /** * 利用串行化深克隆一個對象,把對象以及它的引用讀到流裏,在寫入其餘的對象 * 深克隆在clone方法中也可實現,只要是屬性是對象的,都要單獨調用clone方法, * 若是對象屬性不少,顯得很麻煩,因此通常都是用序列化方式來實現(不理解的 * 能夠單獨交流) * @return * @throws IOException * @throws ClassNotFoundException */
    public Object deepClone() throws IOException,ClassNotFoundException {
        //將對象寫到流裏
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        //從流裏讀回來
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return ois.readObject();
    }

    @Override
    public String toString() {
        return "Husband{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", wife=" + wife +
                '}';
    }
}

複製代碼
package com.lvzhi.clone;

/** * Created by lvzhi on 2018/1/14 */
public class Test {

    //淺克隆
    @org.junit.Test public void testShadowClone() throws Exception {
        Wife wife = new Wife();
        wife.setName("韓梅梅");
        wife.setAge(19);
        Husband husband = new Husband();
        husband.setName("李雷");
        husband.setAge(20);
        husband.setWife(wife);

        //克隆一個對象
        Husband husband1 = (Husband) husband.clone();

        System.out.println("husband,husband1是否爲同一個對象:" + (husband == husband1));

        husband1.setAge(21);
        System.out.println(husband);
        System.out.println(husband1);
        System.out.println("以上結果看出,原對象的基本屬性值age,並無改變。");

        husband1.getWife().setAge(21);
        System.out.println(husband);
        System.out.println(husband1);
        System.out.println("以上結果能夠看出,原對象和被克隆對象的對象屬性都改動了,wife的年齡都是21了。");
        System.out.println("不能作錯誤的測試,譬如:直接又new了一個wife對象,而後,husband1.setWife(newWife),這樣" +
                "husband中對象wife是不會改變的,想不明白的,再問我吧,呵呵");

    }

    //深克隆
    @org.junit.Test public void testDeepClone() throws Exception {
        Wife wife = new Wife();
        wife.setName("韓梅梅");
        wife.setAge(19);
        Husband husband = new Husband();
        husband.setName("李雷");
        husband.setAge(20);
        husband.setWife(wife);

        //克隆一個對象
        Husband husband1 = (Husband) husband.deepClone();

        System.out.println("husband,husband1是否爲同一個對象:" + (husband == husband1));

        husband1.setAge(21);
        System.out.println(husband);
        System.out.println(husband1);
        System.out.println("以上結果看出,原對象的基本屬性值age,並無改變。");

        husband1.getWife().setAge(21);
        System.out.println(husband);
        System.out.println(husband1);
        System.out.println("以上結果能夠看出,原對象什麼都沒變更");

    }

}

複製代碼

23,jvm部分

parallel collector(throughput collector) 並行收集器:使用多線程的方式,利用多CUP來提升GC的效率,主要以到達必定的吞吐量爲目標。 concurrent collector(concurrent low pause collector) 併發收集器:使用多線程的方式,利用多CUP來提升GC的效率,併發完成大部分工做,使得gc pause短。

以上二者主要區別:throughput collector只在young area使用使用多線程,而concurrent low pause collector則在tenured generation也使用多線程

24,java的重寫(Override)與重載(Overload)

方法的重寫規則:

  • 參數列表必須徹底與被重寫方法的相同;
  • 返回類型必須徹底與被重寫方法的返回類型相同;
  • 訪問權限不能比父類中被重寫的方法的訪問權限更低。例如:若是父類的一個方法被聲明爲public,那麼在子類中重寫該方法就不能聲明爲protected。
  • 父類的成員方法只能被它的子類重寫。
  • 聲明爲final的方法不能被重寫。
  • 聲明爲static的方法不能被重寫,可是可以被再次聲明。
  • 子類和父類在同一個包中,那麼子類能夠重寫父類全部方法,除了聲明爲private和final的方法。
  • 子類和父類不在同一個包中,那麼子類只可以重寫父類的聲明爲public和protected的非final方法。
  • 重寫的方法可以拋出任何非強制異常,不管被重寫的方法是否拋出異常。可是,重寫的方法不能拋出新的強制性異常,或者比被重寫方法聲明的更普遍的強制性異常,反之則能夠。
  • 構造方法不能被重寫。 若是不能繼承一個方法,則不能重寫這個方法。

方法重載規則:

  • 被重載的方法必須改變參數列表(參數個數或類型或順序不同)
  • 被重載的方法能夠改變返回類型;
  • 被重載的方法能夠改變訪問修飾符;
  • 被重載的方法能夠聲明新的或更廣的檢查異常;
  • 方法可以在同一個類中或者在一個子類中被重載。
  • 沒法以返回值類型做爲重載函數的區分標準。 若是不能繼承一個方法,則不能重寫這個方法。
相關文章
相關標籤/搜索