1. 程式人生 > >Java最經典知識點總結,看完你都記住的了嗎?

Java最經典知識點總結,看完你都記住的了嗎?

1實現多執行緒的方式有幾種?

其實這個問題並不難,只是在這裡做一個總結。一共有三種。

實現Runnable介面,並實現該介面的run()方法

繼承Thread類,重寫run()方法

實現Callable介面,實現call()方法。

大家可能對前兩種已經很清楚了,重點說下第三種。

Callable介面是屬於Executor框架中的類,Callable介面與Runnable介面類似,但比後者功能更加強大,主要有三點:

Callable可以在任務結束後提供一個返回值,Runnable無法提供這個功能;

Callable的call()方法可以丟擲異常,Runnable介面的run()方法不能丟擲異常;

執行Callable可以得到一個Future物件,Future物件表示非同步計算的結果。它提供了檢查計算是否完成的方法。由於執行緒屬於非同步計算模型,所以無法從其他執行緒中得到方法的返回值。在這種情況下,就可以使用Future來監視目標執行緒呼叫call()方法的情況。當呼叫Futrue的get()方法以獲取結果時,當前執行緒就會阻塞,直到call()方法結束返回結果。

小編相信這裡有很多學習java的朋友,小編整理了一份java方面的學習資料,想要獲取的可以加我的java學習群的喲,928204055。歡迎愛學習Java的你們。

舉個例子,此程式碼在JDK 8 下執行,因為使用了lambda表示式:

package exam;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class CallableAndFuture {

public static void main(String[] args) {

ExecutorService threadPool = Executors.newSingleThreadExecutor();

// 啟動執行緒

Future<String> future = threadPool.submit(() -> "Hello, world");

try {

System.out.println("waiting thread to finish.");

System.out.println(future.get()); // 等待執行緒結束,並獲取返回結果

threadPool.shutdown();

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

}

}

}

2 volatile關鍵字的作用

在Java語言中,有時候為了提高程式的執行效率,編譯器會做一些優化操作,把經常被訪問的變數快取起來,程式在讀取這個變數的時候又有可能直接從暫存器中讀取這個值,而不會去記憶體中讀取。這樣的好處提高了程式的執行效率,但當遇到多執行緒程式設計時,變數的值可能被其他執行緒改變了,而該快取的值不會做相應的改變,從而導致應用程式讀取的值可能與實際的變數值不一致。關鍵字volatile正好解決這個問題,被volatile修飾的變數編譯器不會做優化,每次都會從記憶體讀取。

3程式碼中不同屬性和方法的執行順序

經常會遇到一個這樣的程式碼,new一個子類,其子類以及父類每個屬性和方法的執行順序,具體可以看以下例子:

**

* Java程式初始化工作可以在許多不同的程式碼中來完成,它們執行的順序如下:

* 父類靜態變數

* 父類靜態程式碼塊

* 子類靜態變數

* 子類靜態程式碼塊

* 父類非靜態變數

* 父類非靜態程式碼塊

* 父類建構函式

* 子類非靜態變數

* 子類非靜態程式碼塊

* 子類建構函式

*

*

* 注意,只有方法具有多型性,屬性則沒有。

* @author TurtusLi

*

*/

class BaseI {

int num = 1;

public BaseI() {

this.print();

num = 2;

}

public void print() {

System.out.println("Base.num = " + num);

}

}

public class Example1423 extends BaseI {

int num = 3;

public Example1423() {

this.print();

num = 4;

}

// 去掉這個複寫方法,執行看效果

@Override

public void print() {

System.out.println("Sub.num = " + num);

}

public static void main(String[] args) {

BaseI b = new Example1423();

System.out.println(b.num);

}

}

4 switch語句支援String型別的實現原理

在Java 7 以後,switch語句可以用作String型別上。

從本質來講,switch對字串的支援,其實也是int型別值的匹配。它的實現原理如下:

通過對case後面的String物件呼叫hashCode()方法,得到一個int型別的Hash值,然後用這個Hash值來唯一標識著這個case。

那麼當匹配的時候,首先呼叫這個字串的hashCode()方法,獲取一個Hash值(int型別),用這個Hash值來匹配所有的case,如果沒有匹配成功,說明不存在;如果匹配成功了,接著會呼叫字串的equals()方法進行匹配。

由此看出,String變數不能是null;同時,switch的case子句中使用的字串也不能為null。

5多執行緒同步有幾種實現方法

Java主要提供了三種實現同步機制的方法。

synchronized關鍵字。有兩種用法,可以是synchronized方法和synchronized程式碼塊。

wait和notify方法。

Lock。Lock介面有一個實現類ReentrantLock,也可以實現多執行緒的同步。

6在多執行緒程式設計的時候有哪些注意事項

如果能用volatile代替synchronized,近可能使用volatile。因為被synchronized修飾的方法或程式碼塊在同一時間只能允許一個執行緒訪問,而volatile沒有這個限制,因此使用synchronized會降低併發量。由於volatile無法保證原子性操作,因此在多執行緒的情況下,只有對變數的操作為原子操作的情況下才可以使用volatile。

儘可能減少synchronized塊內的程式碼。

給每一個執行緒定義一個名字,這樣有利於除錯。

儘量使用concurrent容器(ConcurrentHashMap)來代替synchronized容器(Hashtable)。

使用執行緒池來控制多執行緒的執行。

7 fail-fast和fail-safe迭代器的區別是什麼?

他們的主要區別是fail-safe允許在遍歷的過程中對容器的資料進行修改,而fail-fast則不允許。下面分別介紹這兩種迭代器的工作原理。

fail-fast:直接在容器上進行遍歷,在遍歷的過程中,一旦發現容器中的資料被修改了(新增元素、刪除或修改元素),就會丟擲ConcurrentModificationException異常導致遍歷失敗。常見的使用fail-fast的容器有HashMap和ArrayList等。

fail-safe:這種遍歷是基於容器的克隆。因此,對容器中內容的修改不影響遍歷。常見使用fail-safe方式的容器有ConcurrentHashMap和CopyOnWriteArrayList。

8如何能夠使JVM中的虛擬機器棧、堆記憶體和方法區發生記憶體溢位?

關於JVM的知識,有一本非常好的書籍——周志明《深入理解Java虛擬機器:JVM高階特性與最佳實踐(第2版)》,裡面有非常好的介紹。幾乎可以說是Java程式設計師必讀書籍。

虛擬機器棧是執行緒私有的,當建立一個執行緒時,同時會新建一個虛擬機器棧,它描述的是Java方法執行的記憶體模型。 棧中有一個非常重要的概念——棧幀。棧幀用於儲存區域性變量表,運算元棧,方法出口等。

其實棧溢位最簡單的方式是無限遞迴。

堆記憶體是執行緒共享的,是JVM中記憶體管理的最大一塊記憶體,它儲存所有例項化的物件。

堆記憶體溢位最簡單的方式是不停的new物件,GC來不及回收,直到記憶體全部耗盡。

方法區也是記憶體共享的。它用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。

方法區溢位簡單的方式是,呼叫String類的intern()方法,此方法如果在堆區找不到已經存在的String物件的話,就會往方法區中的常量池放一份,然後返回其引用放在堆區。還有一種辦法是不停地載入類。

9在int i =0; i=i++;語句中,i=i++是執行緒安全的嗎?如果不安全,請說明上面操作在JVM中的執行過程,為什麼不安全?說出JDK中哪個類能達到以上程式的效果,並且是執行緒安全且高效的,簡述其原理。

語句i=i++的執行過程:先把i的值取出來放到棧頂,可以理解為引入了第三方變數k,此時,k的值為i,然後執行自增操作,於是i的值變為1,最後執行賦值操作i=k(自增前的值),因此,執行結束後,i的值還是0。從上面的分析得知,i=i++語句的執行過程是由多個操作組成,它不是原子操作,因此,不是執行緒安全的。