下面用JDK動态代理加一點簡單的代碼來示範這個過程:
1、接口
package com.yhouse.modules.daos;
public interface IUserDao {
public String getUserName();
}
2、建立代理
import java.lang.reflect.Proxy;
/**
* 建立代理
* @author clonen.cheng
*
*/
public class Invoker {
public Object getInstance(Class<?> cls){
MethodProxy invocationHandler = new MethodProxy();
Object newProxyInstance = Proxy.newProxyInstance(
cls.getClassLoader(),
new Class[] { cls },
invocationHandler);
return (Object)newProxyInstance;
}
3、運作時調用接口的方法時的實作(這一過程也稱為接口的方法實作)
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MethodProxy implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果傳進來是一個已實作的具體類(本次示範略過此邏輯)
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
t.printStackTrace();
}
//如果傳進來的是一個接口(核心)
} else {
return run(method, args);
}
return null;
/**
* 實作接口的核心方法
* @param method
* @param args
* @return
*/
public Object run(Method method,Object[] args){
//TODO
//如遠端http調用
//如遠端方法調用(rmi)
//....
return "method call success!";
}
4、測試
public class ProxyTest {
public static void main(String[] args) {
IUserDao invoker=(IUserDao)new Invoker().getInstance(IUserDao.class);
System.out.println(invoker.getUserName());
1. 程序和線程之間有什麼不同?
程序是一個獨立的運作環境,它可以被看作是一個程式或者一個應用。而線程是在程序中執行的一個任務。程序是作業系統進行資源配置設定的基本機關,而線程是作業系統進行排程的基本機關。程序讓作業系統的并發性成為可能,而線程讓程序的内部并發成為可能。好比Java運作環境是一個(包含了不同的類和程式的)單一程序。
2. Thread 類中的start() 和run() 方法有什麼差別?
1) start()被用來啟動新的線程,run()不能。
2)start()不能被重複調用,run()可以。
3)start()中的run代碼可以不執行完就繼續執行下面的代碼,即線程轉換,如果直接調用run()必須等待其代碼全部執行完才能繼續執行下面的代碼。
4)start()實作了多線程,run()沒有實作多線程。
3. 在多線程中,什麼是上下文切換?
上下文切換是存儲和恢複CPU狀态的過程,它使得線程執行能夠從中斷點恢複執行。是多任務作業系統和多線程環境的基本特征。
4. Java中的volatile 變量是什麼?
volatile是一個特殊的修飾符,隻有成員變量(類的成員變量、類的靜态成員變量)才能使用它。
被volatile修飾之後就具備了兩層語義:
1)保證了不同線程對這個變量進行操作時的可見性(即一個線程修改了某個變量的值,這新值對其他線程來說是即刻可見的)。
2)禁止進行指令重排序。
5. Java中堆和棧有什麼不同?(相對于線程來說)
棧是一塊和線程緊密相關的記憶體區域。每個線程都有自己的棧記憶體,用于存儲本地變量,方法參數和棧調用,一個線程中存儲的變量對其它線程是不可見的。
堆是所有線程共享的一片公用記憶體區域。對象都在堆裡建立,為了提升效率線程會從堆中弄一個緩存到自己的棧,如果多個線程使用該變量就可能引發問題,這時volatile 變量就可以發揮作用了,它要求線程從主存中讀取變量的值。
6. 什麼是線程池? 為什麼要使用它?
建立線程要花費資源和時間,如果任務來了才建立線程那麼響應時間會變長,而且一個程序能建立的線程數有限。為了避免這些問題,在程式啟動的時候就建立若幹線程來響應處理,它們被稱為線程池,裡面的線程叫工作線程。
7. 死鎖是什麼?如何避免死鎖?
死鎖是指兩個或兩個以上的程序在執行過程中,因争奪資源而造成的一種互相等待的現象,若無外力作用,它們都将無法推進下去。這是一個嚴重的問題,因為死鎖會讓你的程式挂起無法完成任務,死鎖的發生必須滿足以下四個條件:
1)互斥條件:一個資源每次隻能被一個程序使用。
2)請求與保持條件:一個程序因請求資源而阻塞時,對已獲得的資源保持不放。
3)不剝奪條件:程序已獲得的資源,在末使用完之前,不能強行剝奪。
4)循環等待條件:若幹程序之間形成一種頭尾相接的循環等待資源關系。
避免死鎖最簡單的方法就是阻止循環等待條件,将系統中所有的資源設定标志位、排序,規定所有的程序申請資源必須以一定的順序(升序或降序)做操作來避免死鎖。
8. Thread類中的yield方法有什麼作用?
Thread.yield() 方法會使目前線程從運作狀态變為就緒狀态,把運作機會讓給其它相同優先級的線程。它是一個靜态的原生(native)方法而且隻保證目前線程放棄CPU占用而不能保證使其它線程一定能占用CPU,執行yield()的線程有可能會被再次繼續執行的。
9. Java中notify 和 notifyAll有什麼差別?
調用notify時,隻有一個等待線程會被喚醒而且它不能保證哪個線程會被喚醒,這取決于線程排程器。雖然如果你調用notifyAll方法,那麼等待該鎖的所有線程都會被喚醒。
10. Java中interrupted 和 isInterruptedd方法的差別?
interrupted() 和 isInterrupted()的主要差別是前者會将中斷狀态清除而後者不會。
Java多線程的中斷機制是用内部辨別來實作的,調用Thread.interrupt()來中斷一個線程就會設定中斷辨別為true。當中斷線程調用靜态方法Thread.interrupted()來檢查中斷狀态時,中斷狀态會被清零。而非靜态方法isInterrupted()用來查詢其它線程的中斷狀态且不會改變中斷狀态辨別。簡單的說就是任何抛出InterruptedException異常的方法都會将中斷狀态清零。無論如何,一個線程的中斷狀态有有可能被其它線程調用中斷來改變。
11. Java多線程中調用wait() 和 sleep()方法有什麼不同?
sleep()和wait()都是使線程暫停執行一段時間的方法。二者差別為:
1)原理不同。
sleep()方法是Thread類的靜态方法,是線程用來控制自身流程的,它會使此線程暫停執行一段時間,而把執行機會讓給其他線程,等到計時時間一到,此線程會自動蘇醒。而wait()方法是Object類的方法,用于線程間的通信,這個方法會使目前擁有該對象鎖的程序等待,直到其他線程用調用notify()或notifyAll()時才蘇醒過來,開發人員也可以給它指定一個時間使其自動醒來。
2)對鎖的處理機制不同。
由于sleep()方法的主要作用是讓線程暫停一段時間,時間一到則自動恢複,不涉及線程間的通信,是以調用sleep()方法僅僅釋放CPU資源或者讓目前線程停止執行一段時間,但不會釋放鎖。而wait()方法則不同,當調用wait()方法後,線程會釋放掉它所占用的鎖,進而使線程所在對象中的其他synchronized資料可被别的線程使用。
3)使用區域不同。
wait()方法必須放在同步控制方法或者同步語句塊中使用,而sleep方法則可以放在任何地方使用。sleep()方法必須捕獲異常,而wait()、notify()、notifyAll()不需要捕獲異常。在sleep的過程中,有可能被其他對象調用它的interrupt(),産生InterruptedException異常。
由于sleep不會釋放鎖标志,容易導緻死鎖問題的發生,一般情況下,不推薦使用sleep()方法,而推薦使用wait()方法。
12. 有三個線程T1,T2,T3,怎麼確定它們按順序執行?
在多線程中有多種方法讓線程按特定順序執行,你可以用線程類的join()方法在一個線程中啟動另一個線程,另外一個線程完成該線程繼續執行。為了確定三個線程的順序你應該先啟動最後一個(T3調用T2,T2調用T1),這樣T1就會先完成而T3最後完成。
13. 如何建立守護線程?
使用Thread類的setDaemon(true)方法可以将線程設定為守護線程,需要注意的是,需要在調用start()方法前調用這個方法,否則會抛出IllegalThreadStateException異常。
/**
* @author
*/public class DaemonThread {
public static void main(String[] args) {
Thread daemonThread = new Thread(new Runnable() {
@Override
public void run() {
}
});
//設定守護線程
daemonThread.setDaemon(true);
daemonThread.start();
}
}
前提知識:
守護程序(Daemon)是運作在背景的一種特殊程序。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件(百度百科)。
Java線程分為兩類分别為daemon線程(守護線程)和User線程(使用者線程),在JVM啟動時候會調用main函數,main函數所在的線程是一個使用者線程,這個是我們可以看到的線程,其實JVM内部同時還啟動了好多守護線程,比如垃圾回收線程。那麼守護線程和使用者線程有什麼差別那?差別之一是當最後一個非守護線程結束時候,JVM會正常退出,而不管目前是否有守護線程,也就是說守護線程是否結束并不影響JVM的退出。言外之意是隻要有一個使用者線程還沒結束正常情況下JVM就不會退出。
14. 什麼是線程排程器(Thread Scheduler)和時間分片(Time Slicing)?
線程排程器是一個作業系統服務,它負責為Runnable狀态的線程配置設定CPU時間。一旦我們建立一個線程并啟動它,它的執行便依賴于線程排程器的實作。
時間分片是指将可用的CPU時間配置設定給可用的Runnable線程的過程。配置設定CPU時間可以基于線程優先級或者線程等待的時間。線程排程并不受到Java虛拟機控制,是以由應用程式來控制它是更好的選擇(即最好不要讓你的程式依賴于線程的優先級)。
15.什麼是ThreadLocal?
ThreadLocal用于建立線程的本地變量,我們知道一個對象的所有線程會共享它的全局變量,是以這些變量不是線程安全的,我們可以使用同步技術。但是當我們不想使用同步的時候,我們可以選擇ThreadLocal變量。每個線程都會擁有他們自己的Thread變量,它們可以使用get()/set()方法去擷取他們的預設值或者線上程内部改變他們的值。
16.Java線程池中submit() 和 execute()方法有什麼差別?
兩個方法都可以向線程池送出任務,execute()方法的傳回類型是void,它定義在Executor接口中, 而submit()方法可以傳回持有計算結果的Future對象,它定義在ExecutorService接口中,它擴充了Executor接口,其它線程池類像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有這些方法。
17. Java中Runnable和Callable有什麼不同?
Runnable和Callable都代表那些要在不同的線程中執行的任務。Runnable從JDK1.0開始就有了,Callable是在JDK1.5增加的。它們的主要差別是Callable的 call() 方法可以傳回任務執行結果值和抛出異常,而Runnable的run()方法沒有這些功能。Callable可以傳回裝載有計算結果的Future對象。
高并發
1. 什麼是FutureTask?
在Java并發程式中FutureTask表示一個可以取消的異步運算。它有啟動和取消運算、查詢運算是否完成和取回運算結果等方法。隻有當運算完成的時候結果才能取回,如果運算尚未完成get方法将會阻塞。一個FutureTask對象可以對調用了Callable和Runnable的對象進行包裝,由于FutureTask也是調用了Runnable接口是以它可以送出給Executor來執行。