-
進程:(一段程序的執行過程),是一個應用程序,而線程是一個進程中的執行場景/執行單元,一個進程可以啟動多個線程。進程相當于一個公司,線程相當于一個公司的員工。
-
**多線程 **,是指從軟件或者硬件上實現多個線程并發執行的技術。能夠在同一時間執行多于一個線程。在一個程序中,這些獨立運行的程序片段叫作“ 線程”(Thread),利用它編程的概念就叫作“多線程處理”
-
進程A和進程B的內存獨立不共享;線程A和線程B的堆內存和方法區內存共享,但棧內存獨立,一個線程一個棧。
-
java程序中至少有兩個線程并發,一個是垃圾回收線程,一個是執行main方法的主線程。
-
并發執行機制原理:簡單地說就是把一個處理器劃分為若干個短的時間片,每個時間片依次輪流地執行處理各個應用程序,由于一個時間片很短,相對于一個應用程序來說,就好像是處理器在為自己單獨服務一樣,從而達到多個應用程序在同時進行的效果。
2.實現線程的兩種方式
(1)編寫一個類,繼承java.lang.Thread , 重寫run方法
package 多線程;
/*1. 怎么創建線程對象? 怎么啟動線程?】
* new一個線程對象 調用線程對象的start方法
*2. start方法的作用:
* 啟動一個分支線程,在JVM中開辟一個新的棧空間,這段代碼任務(開辟一個新的棧空間)完成以后,start方法就結束了,線程啟動成功了!!!!
* 啟動成功的線程會自動調用run方法,并且run方法在分支棧的棧底部(壓棧)
* run方法在分支棧的棧底部,main方法在主棧的棧底部
*3. 分支棧和主棧 會“并發”執行
*/
public class T1 {
public static void main(String[] args) {//此處是main方法,屬于主線程(在主棧中執行)
//新建一個分支線程對象
MyThread myThread = new MyThread();
//啟動線程:
myThread.start();//啟動線程成功,瞬間結束
for(int i=1;i<=100;i++) {
System.out.println("主線程"+i+"執行");
}
}
//方法體中的代碼永遠都是自上到下的順序執行!!!!
}
class MyThread extends Thread{
//必須重寫run方法
@Override
public void run() {//在此處編寫程序,會運行在分支線程中(分支棧)
for(int i=1;i<=100;i++) {
System.out.println("分支線程"+i+"執行");
}
}
}
/*主線程1執行
分支線程1執行
主線程2執行
分支線程2執行
主線程3執行
分支線程3執行
主線程4執行
分支線程4執行
主線程5執行
分支線程5執行
主線程6執行
分支線程6執行
主線程7執行
分支線程7執行
主線程8執行
分支線程8執行
主線程9執行
主線程10執行
主線程11執行
主線程12執行
主線程13執行
主線程14執行
主線程15執行
主線程16執行
主線程17執行
主線程18執行
分支線程9執行
分支線程10執行
分支線程11執行
分支線程12執行
主線程19執行
主線程20執行
主線程21執行
....*/
(2)編寫一個類,實現java.lang.Runnable接口,實現run方法
package 多線程;
public class T1 {
public static void main(String[] args) {
// 創建一個可運行對象
MyThread myThread = new MyThread();
// 調用Thread類中的構造方法: Thread(Runnable target) 實現將一個可運行對象封裝程一個線程對象
Thread thread = new Thread(myThread);
// 啟動線程
thread.start();
for (int i = 1; i <= 100; i++) {
System.out.println("主線程" + i + "執行");
}
}
}
//這并不是一個線程類,是一個可運行的類,還不是一個線程
class MyThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("分支線程" + i + "執行");
}
}
}
/*主線程1執行
分支線程1執行
主線程2執行
分支線程2執行
主線程3執行
分支線程3執行
主線程4執行
分支線程4執行
主線程5執行
主線程6執行
分支線程5執行
主線程7執行
分支線程6執行
主線程8執行
分支線程7執行
分支線程8執行
分支線程9執行
分支線程10執行
分支線程11執行
分支線程12執行
分支線程13執行
分支線程14執行
...*/
建議使用第二種:這種面向接口編程,一個類除了實現接口外,還可以繼承實現別的類。
也可以采用:匿名內部類的方式
public class T1 {
public static void main(String[] args) {
//創建線程對象
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
System.out.println("分支線程" + i + "執行");
}
}
});
//啟動線程
thread.start();
for (int i = 1; i <= 100; i++) {
System.out.println("主線程" + i + "執行");
}
}
}
3.線程的生命周期
(1)剛new出來的線程對象,稱為“新建狀態”
(2)調用start方法,轉為“就緒狀態”(又叫做可運行狀態,表示當前線程具有搶奪CPU時間片的權利,時間片即執行權)
(3)當線程搶奪到時間片之后,就開始執行run方法,run方法的執行標志著線程進入“運行狀態”。
(4)當之前占有的時間片用完之后,會重新回到“就緒狀態”,繼續搶奪時間片,當搶到時間片后,會接著上一次的代碼繼續往下執行,接下來會重復2,3,4步驟
(5)當run方法執行結束,標示著“死亡狀態”
當在run方法執行過程中,遇到阻塞事件例如用戶輸入,就會由“運行狀態”進入“阻塞狀態”。進入“阻塞狀態”的線程會放棄之前占有的時間片。阻塞解除,會進入“就緒狀態,搶奪時間片。
根據以上,就可以解釋在并發執行主線程和分支線程時,有先有后,有多有少;
線程的時間片較短,“就緒狀態”和“運行狀態”頻繁地交替,給人的錯覺就是并發執行
4. 獲取當前線程對象Thread.currentThread(),線程的名字getName()
package 多線程;
/*1. 線程默認的名字是Thread-0, Thread-1 .....
* 修改線程的名字:線程對象.setName(String name)
* 獲取線程的名字: 線程對象.getName()
*2.獲取當前線程對象:public static native Thread currentThread();
* */
public class T1 {
public static void main(String[] args) {
// 新建一個分支線程對象
MyThread t1 = new MyThread();
// 啟動線程:
t1.start();
// 設置線程的名字
t1.setName("t1");
// 獲取線程的名字
String string = t1.getName();
System.out.println(string);
// 獲取當前線程對象
Thread t = t1.currentThread();// 這個方法出現在main方法當中,所以當前線程就是主線程
System.out.println("線程" + t.getName() + "在執行");// 主線程的名字就是main
MyThread t2 = new MyThread();
t2.setName("t2");
t2.start();
}
}
class MyThread extends Thread {
// 必須重寫run方法
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
// 關鍵是看那個線程對象取調用start方法去啟動此線程,它就是當前線程對象
Thread thread = Thread.currentThread();
System.out.println("分支線程" + thread.getName() + "第" + i + "次" + "執行");
}
}
}
/*t1
分支線程t1第1次執行
線程main在執行
分支線程t1第2次執行
分支線程t1第3次執行
分支線程t1第4次執行
分支線程t2第1次執行
分支線程t2第2次執行
分支線程t1第5次執行
分支線程t2第3次執行
分支線程t2第4次執行
分支線程t2第5次執行
*/
5. 線程的sleep方法
package 多線程;
/*線程的sleep方法:
* 1. static void sleep(long mills)
* 靜態方法,參數是毫秒; 作用是讓當前線程進入休眠,進入阻塞狀態,放棄占有的CPU時間片,讓給其他線程使用
特別注意:sleep方法是靜態的,與是誰調用它無關,在調用時,會自動轉為Thread.sleep
* 此代碼出現在a線程中,a線程進入休眠。
* 1秒 == 1000 毫秒
* 2. public static native void sleep(long millis) throws InterruptedException;
* InterruptedException extends Exception
* 此處屬于編譯時異常,要么throws,要么try catch
* 3. Thread.sleep(時間) 實現間隔特定的時間,去只執行一段特定的代碼 */
public class T1 {
public static void main(String[] args) {
// try {
// Thread.sleep(1000*5);//讓當前線程休眠5秒 當前線程就是此處的主線程
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
// System.out.println("5秒之后代碼執行");
//實現每隔2秒輸出一次
for(int i=0;i<5;i++) {
System.out.println(Thread.currentThread().getName()+"線程執行第"+i+"次");
try {
Thread.sleep(2000);//讓當前線程休眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*main線程執行第0次
main線程執行第1次
main線程執行第2次
main線程執行第3次
main線程執行第4次*/
6. 終止線程的睡眠,就是”喚醒“正在睡眠的線程 interrupt()
package 多線程;
public class T2 {
public static void main(String[] args) {
Thread thread = new Thread(new MyThread2());
thread.setName("t");
thread.start();
// 主線程休眠3秒,3秒后希望喚醒t線程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 喚醒t線程
thread.interrupt();
// 這種中斷睡眠的方式,依靠了java的異常處理機制
// run()中的sleep就會拋異常,catch抓住異常,打印異常信息,整個try catch語句結束,程序繼續往下執行了
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "線程start");
// 此處只能try catch 不可以throws,因為子類重寫不可以拋出比父類更多的異常
// 所以,run方法中的異常不可以throws,因為run方法在父類中沒有拋出任何異常
try {
Thread.sleep(7000);
} catch (InterruptedException e) {
// 打印異常信息
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "線程end");
}
}
/*t線程start
java.lang.InterruptedException: sleep interrupted
t線程end
at java.base/java.lang.Thread.sleep(Native Method)
at 多線程.MyThread2.run(T2.java:33)
at java.base/java.lang.Thread.run(Thread.java:831)
*/
若不想顯示異常信息,直接將catch語句中的打印異常信息的語句注釋掉就可以了
7. 終止一個線程的執行
package 多線程;
public class T2 {
public static void main(String[] args) {
MyThread2 my = new MyThread2();
Thread thread = new Thread(my);
thread.setName("t");
thread.start();
// 模擬5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 終止線程
my.run = false;
}
}
class MyThread2 implements Runnable {
boolean run = true;// 此處設置布爾標記,直接在外面改變此標記
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
if (run) {
System.out.println(Thread.currentThread().getName() + "線程start" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 終止當前線程
return;
}
}
}
}
/*t線程start1
t線程start2
t線程start3
t線程start4
t線程start5*/
浙公網安備 33010602011771號