把java基礎擼一邊,從簡單的開始html
學習多線程,首先要了解多線程。有不少概念仍是要了解一下java
推薦連接:線程和進程的區別,很詳細(若是這個看了,下面的幾段文字能夠過,抄的)多線程
有兩種程,進程和線程併發
進程,是併發執行的進程在執行過程當中分配和管理資源的基本單位,是一個動態概念,競爭計算機系統資源的基本單位。ide
線程,線程是進程的一部分,一個沒有線程的進程能夠被看做是單線程。線程有時又被成爲輕量級進程,也是CPU調度的一個基本單位學習
線程的改變只表明CPU的執行過程的改變,而沒有發生進程所擁有的資源變化。除了CPU以外,計算機的軟硬件資源分配與線程無關,線程只能共享它所屬進程的資源。this
與進程控制表和PCB類似,每一個線程也有本身的線程控制表TCB,而這個TCB中所保存的線程狀態則比PCB表少得多,這些信息主要是相關指針用堆棧(系統棧和用戶棧),寄存器中的狀態數據。spa
進程擁有一個完整的虛擬機地址空間,不依賴於線程而獨立存在;反之,線程是進程的一部分,沒有本身的地址空間,與進程內的其餘線程一塊兒共享給該進程的全部資源。線程
java線程生命週期圖(這圖是我照着畫的):
若是有興趣的話,也能夠畫畫。
如下是我亂逼逼,本身的認識。最好看官方的
線程的生命狀態:新建狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態
阻塞狀態的時候,有兩個定義,等待,和堵塞。等待:(理解)線程要進入的CPU被其餘線程佔用,服務執行該線程的任務。阻塞狀態,代碼notify()方法,或者說,除系統通知外叫醒這個線程的是等待。而堵塞是系統叫醒,好比sleep()。睡眠玩時間以後,系統會通知這個線程結束了。
Thread對象實例化後執行start()方法並無立刻去執行run()方法,而是在在Runnable就緒狀態去搶奪CPU資源開始運行。起跑100米也要準備如下start()後就是須要準的強制CPU資源的那一瞬間,到了Running運行狀態後若是執行完畢或者有異常退出,這個線程也就執行完畢Dead死亡了。
但還有可能在運行過程當中出現堵塞情況,好比sleep()睡眠,join()方法就會出現堵塞,當sleep結束或者join終端,I/O完成的時候會從新到Runnable準備去搶佔CPU資源,而正在執行的Running的線程在yield的時候就與禮讓CPU資源 進入Runnable狀態
在Running狀態的時候synchronize會進入鎖,這個時候會等待處理一下。當同步鎖被釋放的時候會進入Runnable再去搶佔資源
在Running狀態的時候synchronize狀態後wait()釋放鎖等待再到notify()喚醒鎖,再次走當上面剛剛說的堵塞中。這裏就基本瞭解了線程的運行狀態
這是上面我推薦的博客上面的
下面是基本的使用,線程有兩個中重要的類,一個是抽象類,Runnable,一個是Runnable實現類Thread。使用線程的時候通常是new Thread().start();這樣就能夠開啓一個線程
還有就是實現Runnable的類,好比A實現了Runnable類,
A a = new A;
new Thread(a).start();這樣也能夠開啓一個線程。二者的實現不一樣來自於對Thread類不一樣的構造方法不一樣
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}複製代碼
在來看看Runnable類
public interface Runnable {
public abstract void run();
}複製代碼
run()方法裏面就是這個線程的執行內容。
再來看看Thread的start()方法
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
private native void start0();複製代碼
看到native就知道是去調用C代碼
start0()方法會新運行一個線程,新線程會調用run()方法。
public void run() {
if (target != null) {
target.run();
}
}複製代碼
這樣一來就能夠理清楚繼承Thread類和實現Runnable類兩種基本用法的區別
經過繼承Thread類的須要去實現Thread。由於在構建方法中init方法是傳遞的null去賦值的。若是是建立傳遞了Runnable中,是傳遞過去。而且賦值,這樣再run()方法中判斷了target()而且運行了該實現Runnable類的run()方法
知道這個代碼背景,就對線程的使用有了一個概念。
public class Demo1 extends Thread{
public Demo1(String name){
super(name);
}
@Override
public void run() {
while (!interrupted()) {
System.out.println("線程 :"+getName());
}
}
public static void main(String[] age){
Demo1 a = new Demo1("A");
a.start();
}
}複製代碼
繼承Thread的實現
public class Demo2 implements Runnable {
public static void main(String[] age){
Demo2 demo2 = new Demo2();
Thread thread = new Thread(demo2);
thread.start();
}
public void run(){
while (true){
System.out.println("thread running ...");
}
}
}複製代碼
實現抽象類的Runnable