Callable介面及執行緒池
執行緒池
執行緒容器,可設定執行緒分配的數量上限。
將預先建立執行緒物件存入池中,並重用執行緒池中的執行緒物件。
避免頻繁的的建立和銷燬。
常用的執行緒池介面和類
Executor :執行緒池的頂級介面。
ExecutorService :執行緒池介面,可通過submit(Runnable task)提交任務程式碼。
Executors工廠類:通過此類可獲得一個執行緒池。
通過newFixedThreadPool(int nThreads)獲取固定數量的執行緒池。
通過newCachedThreadPool()獲得動態數量的執行緒池,如果不夠 則建立新的,沒有上限。
public class TestThreadPool { public static void main(String[] args) { //執行緒池介面(引用) ---> Executors工具類(工廠類) ExecutorService es = Executors.newFixedThreadPool(4); Runnable task = new MyTask(); es.submit(task); //submit()提交任務 es.submit(task); es.submit(task); es.submit(task); } } //建立介面實現類 class MyTask implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " - " +i); } } }
Callable介面
JDK5加入,與Runnable介面類似,實現之後代表一個執行緒任務。
有泛型返回值,可以宣告異常。
Future介面
概念:
非同步接收ExecutorService.submit()所返回的狀態結果,當中包含了call()的返回值。
方法:
V get()以阻塞形式等待Future的非同步處理結果(call()的返回值)
使用
執行緒池執行Callable介面,Callable返回值由Future接收,Future當中的get() 方法就可以得到非同步返回值。
可以解決併發下的一些統計工作。
public class TestCallable { public static void main(String[] args) throws InterruptedException, ExecutionException { System.out.println("程式開始"); //建立執行緒池 ExecutorService ex = Executors.newFixedThreadPool(3); //介面引用指向實現介面 Callable<Integer> task1 = new MyTask1(); Callable<Integer> task2 = new MyTask2(); Future<Integer> f1 = ex.submit(task1); //得到Callable執行緒介面的返回值 Future<Integer> f2 = ex.submit(task2); Integer result1 = f1.get(); //以阻塞形式等待Future中的非同步處理結果(call的返回值) Integer result2 = f2.get(); //在沒有返回值以前,get無限期等待 System.out.println(result1 + result2); //輸出5050 } } //Mytask1類遵從Callable介面實現call()方法 class MyTask1 implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(1000); //休眠1秒,觀察Future介面get方法 Integer sum = 0; for (int i = 1; i <= 50; i++) { sum += i; } return sum; //返回計算的sum值 } } //Mytask2類遵從Callable介面實現call()方法 class MyTask2 implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(1000); Integer sum = 0; for (int i = 51; i <= 100; i++) { sum += i; } return sum; //返回計算的sum值 } }
同步和非同步的區別:
同步:
形容一次方法呼叫,同步一旦開始,呼叫者必須等待該方法返回,才能繼續。
非同步:
形容一次方法呼叫,非同步一旦開始,像是一次訊息傳遞,呼叫者告知之後立刻返回。二者競爭時間片,併發執行。
Lock介面
提供更多實用性的方法,功能更強大,效能更優越。
常用方法:
void lock() - 獲取鎖,如鎖被佔用,則等待。
boolean tryLock() - 嘗試獲取鎖(成功返回true,失敗返回false,不阻塞)
void unlock() - 釋放鎖
重入鎖
ReentrantLock:Lock介面的實現類,與synchronized一樣具有互斥鎖
功能。
Lock locker = new ReentrantLock(); //建立重入鎖物件
locker.lock(); //開啟鎖
try{
鎖住程式碼塊
}finally{
//考慮可能會出現異常,釋放鎖必須放入finally程式碼塊中,避免無法釋放
locker.unlock(); //釋放鎖
}
讀寫鎖
ReentrantReadWriteLock:
一種支援一寫多讀的同步鎖,讀寫分離,可分別分配讀鎖,寫鎖。
支援多次分配讀鎖,使多個讀操作可以併發執行。
互斥規則:
寫 - 寫:互斥,阻塞。
讀 - 寫:互斥,讀阻塞寫,寫阻塞讀。
讀 - 讀:不互斥,不阻塞。
在讀操作遠遠高於寫操作的環境中,可在保障執行緒安全的情況下,提高執行效率。
//讀寫鎖
class Student {
//建立讀寫鎖物件
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
//建立讀鎖
ReentrantReadWriteLock.ReadLock readLock =rwl.readLock();
//建立寫鎖
ReentrantReadWriteLock.WriteLock writeLock = rwl.writeLock();
private int value; //屬性
//讀方法
public int getValue() throws InterruptedException {
readLock.lock(); //開啟讀鎖
try {
Thread.sleep(1000); //休眠1秒,觀察讀的時間
return value;
} finally {
readLock.unlock(); //關閉讀鎖
}
}
//寫方法
public void setValue(int value) throws InterruptedException {
writeLock.lock(); //開啟寫鎖
try {
Thread.sleep(1000);
this.value = value;
} finally {
writeLock.unlock(); //關閉寫鎖
}
}
}