線程的創建
1.繼承Thread類,重寫run方法;
2.實現Runnable接口 重寫run方法;
區別
使用Runnable接口的更適合面向對象的思想,可以使線程類繼承其他父類。多個線程處理一個Runnable等問題。
join/wait/notify
join 調用此方法的線程在調用之前執行完畢
....thread1.....
thread1.join()
....thread2.....
線程本來是異步的,在沒加入join時候,如果兩個線程同時執行是不知道兩個線程執行的先後順序的,但加入join後,join調用的線程限制性完畢後,在執行join下面的代碼。
wait 與notify 區別
wait 線程休眠,釋放鎖,讓出CPU,進入休眠
notify 喚醒休眠的線程
使用範例
while(conditioin){
try{
wait();
}catch(Exception e){
}
.......
}
另外地方 notify() 喚醒休眠線程
線程的掛起和線程的等待的區別:
- 線程的掛起:被動的,保護現場(頁面和寄存器等),內存交出去,跟中斷一樣
- 線程的等待:主動的,不需要保護線程,內存一直佔用等待某種結果
鎖實現同步Lock/ReentrantLock/ReadWriteLock/ReentrantReadWriteLock
- ReentrantLock的使用
Lock lock = new ReentrantLock();
lock.lock();
...臨界區...
lock.unlock();
注意:unlock解鎖一定要放到try() finilly {…unlock};
- ReentrantReadWriteLock讀寫鎖的使用
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//讀數據時候
readWriteLock.readLock().lock();
//讀臨界區
readWriteLock.readLock.unlock();
....
//寫數據時候
readWriteLock.writeLock().lock();
//寫臨界區
readWriteLock.writeLock.unlock();
讀寫鎖和重入鎖不同的是,讀寫鎖在讀的時候是運行多個線程訪問資源,寫時候只允許一個線程訪問臨界資源。
- 鎖中的多條件使用
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//代碼執行
lock.lock();
while(條件){
try{
condition.await();
}catch(Exceptioin e){
}
}
.....//臨界區代碼
condition.signal();//通知喚醒休眠的線程
lock.unlock();
鎖的多條件使用與前面的wait/notify類似,只是wait/notify與關鍵字synchroniced一起使用,而鎖不需要在方法名前面添加關鍵字。
線程同步輔助類
- CountDownLatch
public CountDownLatch(int count) { }; //參數count爲計數值
public void await() throws InterruptedException { }; //調用await()方法的線程會被掛起,它會等待直到count值爲0才繼續執行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()類似,只不過等待一定的時間後count值還沒變爲0的話就會繼續執行
public void countDown() { }; //將count值減1
基本工作原理:count計數值爲零時候,await()被喚醒往下執行,不然一直阻塞在這裏。
- Semaphore
public void acquire() throws InterruptedException { } //獲取一個許可
public void acquire(int permits) throws InterruptedException { } //獲取permits個許可
public void release() { } //釋放一個許可
public void release(int permits) { } //釋放permits個許可
基本工作原理:獲取信號,相當於鎖;沒有獲取到時候阻塞等待
- CyclicBarier
public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
基本工作原理:所有的線程都到達barier後開始執行
Exchanger 線程數據交互
exchange(V v) //交換數據
基本工作原理:將相同的Exchanger實例在不同的線程中進行數據交換
線程池
線程池就是事先將多個線程對象放到一個容器中,當使用的時候就不用new 線程而是直接去池中拿線程即可,節省了開闢子線程的時間,提高的代碼執行效率。
- 減少在創建和銷燬線程上所花的時間以及系統資源的開銷
- 如不使用線程池,有可能造成系統創建大量線程而導致消耗完系統內存以及”過度切換
Java四種線程池使用方法
- 4.1 newCachedThreadPool
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閒線程,若無可回收,則新建線程。
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(new Runnable() {
@Override
public void run() {
}
});
- 4.2 newFixedThreadPool
創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
ExecutorService executorService1 = Executors.newFixedThreadPool(3);
executorService1.execute(new Runnable() {
@Override
public void run() {
}
});
- 4.3 newScheduledThreadPool
創建一個定長線程池,支持定時及週期性任務執行
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
}
}, 3, TimeUnit.MINUTES);
- 4.4 newSingleThreadExecutor
創建一個單線程化的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
executorService2.execute(new Runnable() {
@Override
public void run() {
}
});
Fork/Join框架
分解合併框架,通過分治技術將問題按照一定的規則拆分成很多小問題來處理。
主要編程步驟:
XXXTask xxxTask = new XXXTask();
ForkJoinPool pool = new ForkJoinPool();
pool.execute(xxxTask);
XXXTask這裏分爲兩種情況:
無返回參數的:
class XXXTask extends RecursiveAction //(RecursiveAction extends ForkJoinTask<Void>)
//然後到實現compute方法,進行分治邏輯處理
有參數的:
class XXXTask extends RecursiveTask<T> //(RecursiveTask<T> extends ForkJoinTask<T>)
//然後到實現compute方法,進行分治邏輯處理,記得返回參數合併問題。直接可以通過task.get()方法拿到返回值。