1、併發與並行編程
併發是同一時間段,多個任務都在執行(單位時間內不必定同時執行);多線程
並行是單位時間內,多個任務同時執行。併發
併發的關鍵是你有處理多個任務的能力,不必定要同時。 而並行的關鍵是你有同時處理多個任務的能力。 ide
2、線程與進程函數
線程是程序執行中一個單一的順序控制流程,是程序執行流的最小單元,是處理器調度和分派的基本單位。一個進程能夠有一個或多個線程,各個線程之間共享程序的內存空間(也就是所在進程的內存空間) ;測試
進程 是一個具備必定獨立功能的程序在一個數據集上的一次動態執行的過程,是操做系統進行資源分配和調度的一個獨立單位,是應用程序運行的載體。this
3、建立線程spa
一、繼承Thread類操作系統
重寫run方法:使用繼承方式的好處是,在run()方法內獲取當前線程直接使用this就能夠了,無須使用Thread.currentThread()方法;很差的地方是Java不支持多繼承,若是繼承了Thread類,那麼就不能再繼承其餘類。另外任務與代碼沒有分離,當多個線程執行同樣的任務時須要多份任務代碼。.net
public class ThreadRuning extends Thread{
public ThreadRuning(String name){
//重寫構造,能夠對線程添加名字
super(name);
}
@Override
public void run() {
while(true){
System.out.println("good time");
//在run方法裏,this表明當前線程
System.out.println(this);
}
}
public static void main(String[] args){
ThreadRuning threadRuning = new ThreadRuning("1111");
threadRuning.start();
}
}
二、實現Runable接口
實現run方法:解決繼承Thread的缺點,沒有返回值
public class RunableTest implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("good time");
}
}
public static void main(String[] args) {
RunableTest runableTest1 = new RunableTest();
RunableTest runableTest2 = new RunableTest();
new Thread(runableTest1).start();
new Thread(runableTest2).start();
}
}
三、實現Callable接口
實現call方法:
public class CallTest implements Callable {
@Override
public Object call() throws Exception {
return "hello world";
}
public static void main(String[] args){
FutureTask<String> futureTask = new FutureTask<String>(new CallTest());
new Thread(futureTask).start();
try {
String result = futureTask.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
使用繼承方式的好處是方便傳參,你能夠在子類裏面添加成員變量,經過set方法設置參數或者經過構造函數進行傳遞,而若是使用Runnable方式,則只能使用主線程裏面被聲明爲final的變量。很差的地方是Java不支持多繼承,若是繼承了Thread類,那麼子類不能再繼承其餘類,而Runable則沒有這個限制。前兩種方式都沒辦法拿到任務的返回結果,可是Callable方式能夠
4、線程的生命週期和狀態
Java 線程在運行的生命週期中的指定時刻只可能指定處於下面幾種不一樣狀態的其中一個狀態:
新建狀態:
使用 new 關鍵字和 Thread 類或其子類創建一個線程對象後,該線程對象就處於新建狀態。它保持這個狀態直到程序 start() 這個線程。
就緒狀態:
當線程對象調用了start()方法以後,該線程就進入就緒狀態。就緒狀態的線程處於就緒隊列中,要等待JVM裏線程調度器的調度。
運行狀態:
若是就緒狀態的線程獲取 CPU 資源,就能夠執行 run(),此時線程便處於運行狀態。處於運行狀態的線程最爲複雜,它能夠變爲阻塞狀態、就緒狀態和死亡狀態。
阻塞狀態:
若是一個線程執行了sleep(睡眠)、suspend(掛起)等方法,失去所佔用資源以後,該線程就從運行狀態進入阻塞狀態。在睡眠時間已到或得到設備資源後能夠從新進入就緒狀態。能夠分爲三種:
等待阻塞:運行狀態中的線程執行 wait() 方法,使線程進入到等待阻塞狀態。
同步阻塞:線程在獲取 synchronized 同步鎖失敗(由於同步鎖被其餘線程佔用)。
其餘阻塞:經過調用線程的 sleep() 或 join() 發出了 I/O 請求時,線程就會進入到阻塞狀態。當sleep() 狀態超時,join() 等待線程終止或超時,或者 I/O 處理完畢,線程從新轉入就緒狀態。
死亡狀態:
一個運行狀態的線程完成任務或者其餘終止條件發生時,該線程就切換到終止狀態。
5、線程死鎖
死鎖:兩個或者兩個以上的線程在執行的過程當中,因爭奪資源產生的一種互相等待的現象。
以下代碼(代碼源自《Java多線程編程核心技術》):
public class DeadThreadDemo implements Runnable{
public String username;
public Object lock1 = new Object();
public Object lock2 = new Object();
public void setFlag(String username) {
this.username = username;
}
@Override
public void run(){
if(username.equals("a")) {
synchronized (lock1) {
try {
System.out.println("username = " + username);
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("按 lock1->lock2代碼 順序執行了");
}
}
}
if(username.equals("b")) {
synchronized (lock2) {
try {
System.out.println("username = " + username);
Thread.sleep(3000);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("按lock2->lock1代碼順序執行了");
}
}
}
}
}
測試類:
public class DeadThreadTest {
public static void main(String[] args) {
try {
DeadThreadDemo dtd1 = new DeadThreadDemo();
dtd1.setFlag("a");
Thread thread1 = new Thread(dtd1);
thread1.start();
Thread.sleep(100);
dtd1.setFlag("b");
Thread thread2 = new Thread(dtd1);
thread2.start();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
輸出:
username = a
username = b
線程 a 經過 synchronized (lock1) 得到 lock1 的監視器鎖,而後經過thread.sleap(3000); 讓線程 a 休眠 3s 爲的是讓線程 b 獲得執行而後獲取到 lock2 的監視器鎖。線程 a 和線程 b 休眠結束了都開始企圖請求獲取對方的資源,而後這兩個線程就會陷入互相等待的狀態,這也就產生了死鎖。上面的例子符合產生死鎖的四個必要條件。
死鎖產生的四個條件:
互斥條件: 該資源任意一個時刻只由一個線程佔用;
請求與保持條件:一個線程因請求資源而阻塞,對已得到的資源保持不放;
不剝奪條件:線程已經得到的資源在未使用完以前不能被其餘線程強行剝奪,只由本身使用完畢後才釋放資源;
循環等待條件:若干線程之間造成一種頭尾相接的循環等待資源關係。
避免死鎖的四個方法:
破壞互斥條件:這個條件咱們沒有辦法破壞,由於咱們用鎖自己就是想讓他們互斥的(臨界資源須要互斥訪問)。 破壞請求與保持條件:一次性申請全部的資源 破壞不剝奪條件:佔用部分資源的線程進一步申請其餘資源時,若是申請不到,能夠主動釋放它佔有的資源。 破壞循環等待條件:靠按順序申請資源來預防。按照某一順序申請資源,釋放資源則反序釋放。破壞循環等待條件。