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++語句的執行過程是由多個操作組成,它不是原子操作,因此,不是執行緒安全的。