掌握Java中的多線程,必須掌握Java中的各類鎖,以及瞭解Java中線程池的運用。關於Java多線程基礎總結能夠參考個人這篇博文Java多線程總結(一)多線程基礎html
轉載請註明出處——http://www.cnblogs.com/zrtqsk/p/3784049.html,謝謝。java
1、Java中鎖緩存
什麼是鎖。鎖就是爲了保護資源,防止多個線程同時操做資源時出錯的機制。多線程
咱們先來看一下鎖的類圖:併發
如圖,Java中的鎖有兩個主要的根接口——Lock和ReadWriteLock,分別表示鎖和讀寫鎖。其中Lock的主要實現類是ReetrantLock。ReadWriteLock的主要實現類是ReetrantReadWriteLock。而ReetrantReadWriteLock讀寫鎖是經過兩個內部類——ReadLock和WriteLock實現的,其中ReadLock是共享鎖,WriteLock是獨佔鎖。這兩個內部類都實現了Lock接口。ide
(1)、Java中的鎖主要有如下幾種概念:函數
一、同步鎖 post
同一時刻,一個同步鎖只能被一個線程訪問。以對象爲依據,經過synchronized關鍵字來進行同步,實現對競爭資源的互斥訪問。this
二、獨佔鎖(可重入的互斥鎖) spa
互斥,即在同一時間點,只能被一個線程持有;可重入,便可以被單個線程屢次獲取。什麼意思呢?根據鎖的獲取機制,它分爲「公平鎖」和「非公平鎖」。Java中經過ReentrantLock實現獨佔鎖,默認爲非公平鎖。
三、公平鎖
是按照經過CLH等待線程按照先來先得的規則,線程依次排隊,公平的獲取鎖,是獨佔鎖的一種。Java中,ReetrantLock中有一個Sync類型的成員變量sync,它的實例爲FairSync類型的時候,ReetrantLock爲公平鎖。設置sync爲FairSync類型,只需——Lock lock = new ReetrantLock(true)。
四、非公平鎖
是當線程要獲取鎖時,它會無視CLH等待隊列而直接獲取鎖。ReetrantLock默認爲非公平鎖,或——Lock lock = new ReetrantLock(false)。
五、共享鎖
能被多個線程同時獲取、共享的鎖。即多個線程均可以獲取該鎖,對該鎖對象進行處理。典型的就是讀鎖——ReentrantReadWriteLock.ReadLock。即多個線程均可以讀它,並且不影響其餘線程對它的讀,可是你們都不能修改它。CyclicBarrier, CountDownLatch和Semaphore也都是共享鎖。
六、讀寫鎖
維護了一對相關的鎖,「讀取鎖」用於只讀操做,它是「共享鎖」,能同時被多個線程獲取。「寫入鎖」用於寫入操做,它是「獨佔鎖」,只能被一個線程鎖獲取。Java中,讀寫鎖爲ReadWriteLock 接口定義,其實現類是ReentrantReadWriteLock,包括內部類ReadLock和WriteLock。方法readLock()、writeLock()分別返回度操做的鎖和寫操做的鎖。
(至於「死鎖」,並非一種鎖,而是一種狀態,即兩個線程互相等待對方釋放同步監視器的時候,雙方都沒法繼續進行,形成死鎖。)
鎖的用法主要就是下面的流程:
//先獲得lock lock.lock();//而後獲取鎖 try { //各類控制操做 }catch(Exception e){ }finally { lock.unlock();//解鎖 }
能夠看到,這樣的用法比synchronized關鍵字依據對象同步,要方便簡單的多。
(2)LockSupport和Condition
一、LockSupport
是用來建立鎖和其餘同步類的基本線程阻塞原語。 LockSupport中的靜態方法park() 和 unpark() 的做用分別是阻塞線程和解除阻塞線程,而不會致使死鎖。演示以下:
package lock; import java.util.concurrent.locks.LockSupport; public class LockSupportTest { static Thread mainThread = null; public static void main(String[] args) { //獲取主線程 mainThread = Thread.currentThread(); //新建線程並啓動 MyThread thread1 = new MyThread("thread1"); thread1.start(); //模擬線程工做開始 System.out.println(Thread.currentThread().getName() + "-----》 runs now!"); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "-----》 running step " + i); //當前線程睡眠1秒 sleepOneSecond(); if(i == 2){ System.out.println(Thread.currentThread().getName() + "-----》 now pack main thread——————————"); //讓主線程阻塞 LockSupport.park(); } } System.out.println(Thread.currentThread().getName() + "-----》 run over!"); } /**當前線程暫停一秒鐘 */ public static void sleepOneSecond(){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } static class MyThread extends Thread { public MyThread(String name){ super(name); } @Override public void run() { synchronized (this) { //模擬工做開始 System.out.println(Thread.currentThread().getName() + "-----》 runs now!"); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "-----》 running step " + i); //當前線程睡眠1秒 sleepOneSecond(); } //模擬工做結束 System.out.println(Thread.currentThread().getName() + "-----》 run over!"); } System.out.println(Thread.currentThread().getName() + "-----》 now unpack main thread———————— "); //解除主線程的阻塞 LockSupport.unpark(mainThread); } } }
結果以下:
thread1-----》 runs now! thread1-----》 running step 0 main-----》 runs now! main-----》 running step 0 thread1-----》 running step 1 main-----》 running step 1 main-----》 running step 2 thread1-----》 running step 2 main-----》 now pack main thread—————————— thread1-----》 running step 3 thread1-----》 running step 4 thread1-----》 run over! thread1-----》 now unpack main thread———————— main-----》 running step 3 main-----》 running step 4 main-----》 run over!
二、Condition
對鎖進行精確的控制,可用來代替Object中的wait、notify、notifyAll方法,須要和Lock聯合使用。能夠經過await(),signal()來休眠、喚醒線程。建立方式:Condition condition = lock.newCondition();
演示懶得本身寫了,參照http://www.cnblogs.com/skywang12345/p/3496716.html,以下:
package LockSupportTest; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ConditionTest { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) { ThreadA ta = new ThreadA("ta"); lock.lock(); // 獲取鎖 try { System.out.println(Thread.currentThread().getName()+" start ta"); ta.start(); System.out.println(Thread.currentThread().getName()+" block"); condition.await(); // 等待 System.out.println(Thread.currentThread().getName()+" continue"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); // 釋放鎖 } } static class ThreadA extends Thread{ public ThreadA(String name) { super(name); } public void run() { lock.lock(); // 獲取鎖 try { System.out.println(Thread.currentThread().getName()+" wakup others"); condition.signal(); // 喚醒「condition所在鎖上的其它線程」 } finally { lock.unlock(); // 釋放鎖 } } } }
結果以下:
main start ta main block ta wakup others main continue
如上,用起來挺簡單的。
2、線程池
咱們先來看一下線程池的類圖:
一、介紹
可見,線程池的主要是由一個Executor接口統籌的。這個接口表明一個執行者,是一個典型的命令模式的運用。這個接口只有一個方法void execute(Runnable command),提交併執行任務。
ExecuteService顧名思義,指的是Executor的服務類,繼承了Executor接口,提供了更詳細的控制線程的方法。
AbstractExecutorService是一個抽象類,實現了ExecutorService大部分的方法。
而咱們最經常使用的ThreadPoolExecutor則繼承了ExecutorService。
ForkJoinPool是JDK7新增的線程池,也是繼承了這個線程類。
ScheduledExecutorService這個接口繼承了ExecutorService,比ExecutorService新增了「延時」和「週期執行」的功能。
ScheduledThreadPoolExecutor這個類則實現了ScheduledExecutorService接口,且繼承了ThreadPoolExecutor,新增了「延時」和「週期執行」的功能。
Executors是一個線程池的工廠類,提供一系列靜態方法,用於建立各類不一樣功能的線程池或線程相關的對象。
而線程池的使用,最基本的就是以下:
// 建立各類線程 Thread thread1 = new MyThread(); Thread thread2 = new MyThread(); Thread thread3 = new MyThread(); // 建立線程池pool // 將線程放入池中進行執行 pool.execute(thread1 ); pool.execute(thread2 ); pool.execute(thread3 ); // 關閉線程池 pool.shutdown();
二、ForkJoinPool
可見,整個線程池系列,說白了,也就3個類ThreadPoolExecutor、ScheduledThreadPoolExecutor、ForkJoinPool。一個是普通線程池,一個是新增了「延時」和「週期執行」的功能的線程池。那麼ForkJoinPool是什麼呢?
ForkJoinPool爲了是解決如今、將來計算機多核的問題。ExecuteService其餘實現類基本都是基於單核下執行的,解決的是併發問題,而ForkJoinPool解決的是並行問題。ExcuteService中處於後面的任務須要等待前面任務執行後纔有機會執行,而ForkJoinPool會採用work-stealing模式幫助其餘線程執行任務。work-stealing模式——全部在池中的線程嘗試去執行其餘線程建立的子任務,這樣就不多有線程處於空閒狀態,很是高效。
ForkJoinPool除了能夠執行Runnable任務外,還能夠執行ForkJoinTask任務,即ForkJoinPool的execute方法能夠傳入一個ForkJoinTask對象,這個任務對象跟Runnable的不一樣是,ForkJoinTask被放到線程內部的隊列裏面,而普通的Runnable任務被放到線程池的隊列裏面了。
須要詳細瞭解ForkJoinPool,能夠參考http://blog.csdn.net/aesop_wubo/article/details/10300273。
三、Executors
Executors是一個線程池的工廠類,提供一系列靜態方法,用於建立各類不一樣功能的線程池或線程相關的對象。
主要有以下的幾個靜態方法:
newCachedThreadPool() : 建立一個具備緩存功能的線程池,系統根據須要建立線程,這些線程被緩存在線程池中。
newFixedThreadPool(int nThreads) : 建立一個可重用的,具備固定線程數的線程池。
newSingleThreadExecutor() : 建立一個只有一個單線程的線程池。
newScheduledThreadPool(int corePoolSize) : 建立具備指定數目的線程池,能夠指定延時後執行任務,即便線程空閒,也被保持在線程池內。
newSingleThreadScheduledExecutor() : 建立一個只有一個單線程的線程池,能夠指定延時後執行任務。
四、線程池的狀態
線程池的狀態有五種——RUNNING, SHUTDOWN, STOP, TIDYING, TERMINATED
(圖片出處:http://www.cnblogs.com/skywang12345/p/3509960.html)
RUNNING : 線程池處在RUNNING狀態時,可以接收新任務,以及對已添加的任務進行處理。
SHUTDOWN : 線程池處在SHUTDOWN狀態時,不接收新任務,但能處理已添加的任務。
STOP : 線程池處在STOP狀態時,不接收新任務,不處理已添加的任務,而且會中斷正在處理的任務。
TIDYING : 當全部的任務已終止,ctl記錄的"任務數量"爲0,線程池會變爲TIDYING狀態。 當線程池變爲TIDYING狀態時,會執行鉤子函數terminated()。terminated()在ThreadPoolExecutor類中是空 的,若用戶想在線程池變爲TIDYING時,進行相應的處理;能夠經過重載terminated()函數來實現。
TERMINATED : 線程池完全終止,就變成TERMINATED狀態。
參考:http://www.cnblogs.com/skywang12345/p/java_threads_category.html
http://blog.csdn.net/aesop_wubo/article/details/10300273
若是以爲本文還不錯的話,麻煩點擊推薦哦!謝謝啦!