最近對Core Java基礎作了一些學習。有本身的看法,也有別人的總結,供你們參考。java
其實這個問題並不難,只是在這裏作一個總結。一共有三種。程序員
Runnable
接口,並實現該接口的run()
方法Thread
類,重寫run()
方法Callable
接口,實現call()
方法。你們可能對前兩種已經很清楚了,重點說下第三種。
Callable
接口是屬於Executor
框架中的類,Callable
接口與Runnable
接口相似,但比後者功能更增強大,主要有三點:算法
Callable
能夠在任務結束後提供一個返回值,Runnable
沒法提供這個功能;Callable
的call()
方法能夠拋出異常,Runnable
接口的run()
方法不能拋出異常;Callable
能夠獲得一個Future
對象,Future
對象表示異步計算的結果。它提供了檢查計算是否完成的方法。因爲線程屬於異步計算模型,因此沒法從其餘線程中獲得方法的返回值。在這種狀況下,就可使用Future
來監視目標線程調用call()
方法的狀況。當調用Futrue
的get()
方法以獲取結果時,當前線程就會阻塞,直到call()
方法結束返回結果。舉個例子,此代碼在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(); } } }
volatile
關鍵字的做用在Java語言中,有時候爲了提升程序的運行效率,編譯器會作一些優化操做,把常常被訪問的變量緩存起來,程序在讀取這個變量的時候又有可能直接從寄存器中讀取這個值,而不會去內存中讀取。這樣的好處提升了程序的運行效率,但當遇到多線程編程時,變量的值可能被其餘線程改變了,而該緩存的值不會作相應的改變,從而致使應用程序讀取的值可能與實際的變量值不一致。關鍵字volatile
正好解決這個問題,被volatile
修飾的變量編譯器不會作優化,每次都會從內存讀取。緩存
常常會遇到一個這樣的代碼,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); } }
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。
Java主要提供了三種實現同步機制的方法。
synchronized
關鍵字。有兩種用法,能夠是synchronized
方法和synchronized
代碼塊。wait
和notify
方法。Lock
。Lock
接口有一個實現類ReentrantLock
,也能夠實現多線程的同步。volatile
代替synchronized
,近可能使用volatile
。由於被synchronized
修飾的方法或代碼塊在同一時間只能容許一個線程訪問,而volatile
沒有這個限制,所以使用synchronized
會下降併發量。因爲volatile
沒法保證原子性操做,所以在多線程的狀況下,只有對變量的操做爲原子操做的狀況下才可使用volatile
。synchronized
塊內的代碼。ConcurrentHashMap
)來代替synchronized
容器(Hashtable
)。他們的主要區別是fail-safe容許在遍歷的過程當中對容器的數據進行修改,而fail-fast則不容許。下面分別介紹這兩種迭代器的工做原理。
fail-fast:直接在容器上進行遍歷,在遍歷的過程當中,一旦發現容器中的數據被修改了(添加元素、刪除或修改元素),就會拋出ConcurrentModificationException
異常致使遍歷失敗。常見的使用fail-fast的容器有HashMap
和ArrayList
等。
fail-safe:這種遍歷是基於容器的克隆。所以,對容器中內容的修改不影響遍歷。常見使用fail-safe方式的容器有ConcurrentHashMap
和CopyOnWriteArrayList
。
關於JVM的知識,有一本很是好的書籍——周志明《深刻理解Java虛擬機:JVM高級特性與最佳實踐(第2版)》,裏面有很是好的介紹。幾乎能夠說是Java程序員必讀書籍。
虛擬機棧是線程私有的,當建立一個線程時,同時會新建一個虛擬機棧,它描述的是Java方法執行的內存模型。 棧中有一個很是重要的概念——棧幀。棧幀用於保存局部變量表,操做數棧,方法出口等。
其實棧溢出最簡單的方式是無限遞歸。
堆內存是線程共享的,是JVM中內存管理的最大一塊內存,它保存全部實例化的對象。
堆內存溢出最簡單的方式是不停的new對象,GC來不及回收,直到內存所有耗盡。
方法區也是內存共享的。它用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。
方法區溢出簡單的方式是,調用String
類的intern()
方法,此方法若是在堆區找不到已經存在的String
對象的話,就會往方法區中的常量池放一份,而後返回其引用放在堆區。還有一種辦法是不停地加載類。
int i =0; i=i++;
語句中,i=i++
是線程安全的嗎?若是不安全,請說明上面操做在JVM中的執行過程,爲何不安全?說出JDK中哪一個類能達到以上程序的效果,而且是線程安全且高效的,簡述其原理。語句i=i++
的執行過程:先把i
的值取出來放到棧頂,能夠理解爲引入了第三方變量k
,此時,k
的值爲i
,而後執行自增操做,因而i
的值變爲1
,最後執行賦值操做i=k
(自增前的值),所以,執行結束後,i
的值仍是0
。從上面的分析得知,i=i++
語句的執行過程是由多個操做組成,它不是原子操做,所以,不是線程安全的。
在Java中,++i
和i++
操做並非線程安全的,在使用的時候,不可避免地會用到synchronized
關鍵字。而AtomicInteger
是一個提供原子操做的Integer
類,它提供了線程安全且高效的原子操做,是線程安全的,其底層的原理是利用處理器的CAS(Compare And Swap,比較與交換,一種有名的無鎖算法)操做來檢測棧中的值是否被其餘線程改變,若是被改變,則CAS操做失敗。這種實現方法在CPU指令級別實現了原子操做,所以,它比使用synchronized來實現同步效率更高。