Java多執行緒基礎
- 1.多執行緒概述
- 實作執行緒的兩種方式
- 繼承Thread類
- 實作Runnable介面
- 2.執行緒生命周期
- 獲取執行緒的名字和執行緒物件
- 3.執行緒的休眠
- sleep方法
- 終止執行緒的休眠
- 強行終止一個執行緒,
- 合理的終止一個執行緒的執行
- 4.執行緒調度
- 執行緒調度概述
- 執行緒優先級
- 執行緒讓位
- 執行緒合并
1.多執行緒概述
1、什么是行程?什么是執行緒?
??行程是一個應用程式(1個行程是一個軟體),
??執行緒是一個行程中的執行場景/執行單元,
??一個行程可以啟動多個執行緒,
2、對于java程式來說,當在DOS命令視窗中輸入:java HelloWorld 回車之后,
??會先啟動JVM,而JVM就是一個行程,
??JVM再啟動一個主執行緒呼叫main方法,
??同時再啟動一個垃圾回收執行緒負責看護,回收垃圾,
??最起碼,現在的java程式中至少有兩個執行緒并發,
??一個是垃圾回收執行緒,一個是執行main方法的主執行緒,
3、行程和執行緒是什么關系?舉個例子
??阿里巴巴:行程
??馬云:阿里巴巴的一個執行緒
??童文紅:阿里巴巴的一個執行緒
??京東:行程
??強東:京東的一個執行緒
??妹妹:京東的一個執行緒
??行程可以看做是現實生活當中的公司,
??執行緒可以看做是公司當中的某個員工,
??注意:
????行程A和行程B的記憶體獨立不共享,(阿里巴巴和京東資源不會共享的!)
??魔獸游戲是一個行程
??酷狗音樂是一個行程
??這兩個行程是獨立的,不共享資源,
??執行緒A和執行緒B呢?
??在java語言中:
????執行緒A和執行緒B,堆記憶體和方法區記憶體共享,
????但是堆疊記憶體獨立,一個執行緒一個堆疊,
??假設啟動10個執行緒,會有10個堆疊空間,每個堆疊和每個堆疊之間,
??互不干擾,各自執行各自的,這就是多執行緒并發,
??火車站,可以看做是一個行程,
??火車站中的每一個售票視窗可以看做是一個執行緒,
??我在視窗1購票,你可以在視窗2購票,你不需要等我,我也不需要等你,
??所以多執行緒并發可以提高效率,
java中之所以有多執行緒機制,目的就是為了提高程式的處理效率,
4、思考一個問題:
??使用了多執行緒機制之后,main方法結束,是不是有可能程式也不會結束,
??main方法結束只是主執行緒結束了,主堆疊空了,其它的堆疊(執行緒)可能還在壓堆疊彈堆疊,

5、分析一個問題:對于單核的CPU來說,真的可以做到真正的多執行緒并發嗎?
??對于多核的CPU電腦來說,真正的多執行緒并發是沒問題的,
??4核CPU表示同一個時間點上,可以真正的有4個行程并發執行,
??什么是真正的多執行緒并發?
??t1執行緒執行t1的,
??2執行緒執行t2的,
??t1不會影響t2,t2也不會影響t1,這叫做真正的多執行緒并發,
??單核的CPU表示只有一個大腦:
??不能夠做到真正的多執行緒并發,但是可以做到給人一種“多執行緒并發”的感覺,
??對于單核的CPU來說,在某一個時間點上實際上只能處理一件事情,但是由于
??CPU的處理速度極快,多個執行緒之間頻繁切換執行,跟人來的感覺是:多個事情同時在做!!!!!
????執行緒A:播放音樂
????執行緒B:運行魔獸游戲
??執行緒A和執行緒B頻繁切換執行,人類會感覺音樂一直在播放,游戲一直在運行,
??給我們的感覺是同時并發的,
??電影院采用膠卷播放電影,一個膠卷一個膠卷播放速度達到一定程度之后,人類的眼睛產生了錯覺,感覺是影片的,這說明人類的反應速度很慢,就像一根鋼針扎到手上,到最終感覺到疼,這個程序是需要“很長的”時間的,在這個期間計算機可以進行億萬次的回圈,所以計算機的執行速度很快,
實作執行緒的兩種方式
繼承Thread類
java語言中,實作執行緒有兩種方式,那兩種方式呢?
java支持多執行緒機制,并且java已經將多執行緒實作了,我們只需要繼承就行了,
第一種方式:撰寫一個類,直接繼承java.lang.Thread,重寫run方法,
怎么創建執行緒物件?new就行了
怎么啟動執行緒?呼叫執行緒物件的start方法
注意:方法體當中的代碼永遠都是自上而下的順序依次逐行執行,
public class ThreadTest01 {
public static void main(String[] args) {
//這里是main方法,這里的代碼屬于主執行緒,在主堆疊中運行
//新建一個分支執行緒物件
MyThread myThread =new MyThread();
//啟動執行緒,
//start()方法作用是:啟動一個分支執行緒,
//在jvm中開辟一個新的堆疊空間,這段代碼任務完成后,瞬間就結束了,
//這段代碼的任務只是為了開啟一個新的堆疊空間,
//只要新的堆疊空間開出來,start()方法就結束了,執行緒就啟動成功了,
//啟動成功的執行緒會自動呼叫run方法,并且run方法在分支堆疊的堆疊底部(壓堆疊)
//run方法在分支堆疊的堆疊底部,main方法在主堆疊的堆疊底部,run和main是平級的
//myThread.run(); //不會啟動執行緒,不會分配新的分支堆疊
myThread.start();
//這里的代碼還是運行在主執行緒中
for(int i=0;i<1000;i++){
System.out.println("主執行緒---->"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run() {
//撰寫程式,這段程式運行在分支執行緒中(分支堆疊)
for(int i=0;i<1000;i++){
System.out.println("分支執行緒---->"+i);
}
}
}
以下程式的輸出結果有這樣的特點:
有先有后,有多有少,


總結:
第一種方式:撰寫一個類,直接繼承java.lang.Thread,重寫run方法,
// 定義執行緒類
public class MyThread extends Thread{
public void run(){
}
}
// 創建執行緒物件
MyThread t = new MyThread();
// 啟動執行緒,
t.start();
實作Runnable介面
撰寫一個類,實作java.lang.Runnable介面,實作run方法,
public class ThreadTest02 {
public static void main(String[] args) {
//創建一個可運行的物件,將可運行的物件封裝成一個執行緒物件
Thread t=new Thread(new MyRunnable());
//啟動執行緒
t.start();
for(int i=0;i<100;i++){
System.out.println("主執行緒---->"+i);
}
}
}
//這并不是一個執行緒類,是一個可運行的類,它還不是一個執行緒,
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("分支執行緒---->"+i);
}
}
}
總結:
// 定義一個可運行的類
public class MyRunnable implements Runnable {
public void run(){
}
}
// 創建執行緒物件
Thread t = new Thread(new MyRunnable());
// 啟動執行緒
t.start();
注意:第二種方式實作介面比較常用,因為一個類實作了介面,它還可以去繼承其它的類,更靈活,
采用匿名內部類的方式
public class ThreadTest03 {
public static void main(String[] args) {
//創建執行緒物件,采用匿名內部類方式
//這是通過一個沒有名字的類,new出來的物件,介面是沒有辦法new物件的
Thread t=new Thread(new Runnable(){
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("分支執行緒---->"+i);
}
}
});
t.start();
for(int i=0;i<100;i++){
System.out.println("主執行緒---->"+i);
}
}
}
2.執行緒生命周期
關于執行緒物件的生命周期?
新建狀態
就緒狀態
運行狀態
阻塞狀態
死亡狀態
獲取執行緒的名字和執行緒物件
1.怎么獲取當前執行緒物件
??static Thread currentThread()
??Thread t=Thread.currentThread()
??t代表當前執行緒物件
2.獲取執行緒物件的名字
??執行緒物件.getName()
3.修改執行緒物件的名字
??執行緒物件.setName(“執行緒的名字”);
4.當執行緒沒有設定名字的時候,默認的名字有什么規律
??Thread-0
??Thread-1
public class ThreadTest04 {
public static void main(String[] args) {
//currentThread就是當前執行緒物件
//這個代碼出現在main方法中,所以當前執行緒就是主執行緒
Thread currentThread=Thread.currentThread();
System.out.println(currentThread.getName());
//創建執行緒物件
MyThread2 t=new MyThread2();
//設定執行緒物件的名字
t.setName("tttt");
//獲取執行緒的名字
String tName=t.getName();
System.out.println(tName); //默認名為Thread-0
MyThread2 t2=new MyThread2();
System.out.println(t2.getName()); //默認名為Thread-1
//啟動執行緒
t.start();
}
}
class MyThread2 extends Thread{
@Override
public void run() {
//current就是當前執行緒物件,當前執行緒是誰呢
//當t1執行緒執行run方法,當前執行緒就是t1
//當t2執行緒執行run方法,當前執行緒就是t2
Thread currentThread=Thread.currentThread();
System.out.println("分支執行緒"+currentThread.getName());
for(int i=0;i<100;i++){
System.out.println("分支執行緒"+i);
}
}
}
3.執行緒的休眠
sleep方法
??static void sleep(long mills)
??1.靜態方法 Thread.sleep(1000)
??2.引數是毫秒
??3.作用:讓當前執行緒進入休眠,進入”阻塞狀態“,放棄占用的CPU時間片,讓給其他執行緒使用
??這行代碼出現在A執行緒中,A執行緒就會進入休眠,
??這行代碼出現在B執行緒中,B執行緒就會進入休眠,
??4.Thread.sleep()方法,可以做到這種效果
??間隔特定的時間,去執行一段特定的代碼,每隔多久執行一次,
public class ThreadTest05 {
public static void main(String[] args) {
// //讓當前執行緒進入休眠,睡眠5秒
// //當前執行緒是主執行緒
// try {
// Thread.sleep(1000*5);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// //5秒之后執行這里的代碼
// System.out.println("hello,world");
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
//睡眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
sleep方法是靜態方法,它是讓當前執行緒進入休眠,這行代碼出現在哪個執行緒中,哪個執行緒就會睡眠,
sleep睡眠太久了,如果希望半道上醒來,你應該怎么辦?也就說怎么叫醒一個正在睡眠的執行緒
注意:這個不是中斷執行緒的執行,是終止執行緒的睡眠
用interrupt()方法終止t執行緒的睡眠(這種中斷睡眠的方式依靠了java的例外機制)
終止執行緒的休眠
public class ThreadTest07 {
public static void main(String[] args) {
Thread t=new Thread(new MyRunnable2());
t.setName("t");
t.start();
//希望5秒后,t執行緒醒來(5秒之后主執行緒手里的活兒干完了)
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//終止t執行緒的睡眠(這種中斷睡眠的方式依靠了java的例外機制)
t.interrupt(); //干擾,一盆冷水過去!
}
}
class MyRunnable2 implements Runnable{
//重點:run()當中的例外不能throw,只能trycatch
//因為run()方法在父類中沒有拋出任何例外,子類不能比父類拋出更多的例外
public void run() {
System.out.println(Thread.currentThread().getName()+"---->begin");
try {
//睡眠1小時
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"---->end");
}
}

中間間隔很短的時間
強行終止一個執行緒,
stop()方法這種方式存在很大的缺點:容易丟失資料,因為這種方式是直接將執行緒殺死了,
執行緒原有保存的資料會丟失,不建議使用
終止執行緒的執行
public class ThreadTest08 {
public static void main(String[] args) {
Thread t=new Thread(new MyRunnable3());
t.setName("t");
t.start();
//模擬5秒
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//5秒之后強行終止t執行緒
t.stop();//已過時,不建議使用,
}
}
class MyRunnable3 implements Runnable{
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

合理的終止一個執行緒的執行
怎么合理的終止一個執行緒的執行,這種方式是很常用的
public class ThreadTest09 {
public static void main(String[] args) {
MyRunnable4 r=new MyRunnable4();
Thread t=new Thread(r);
t.setName("t");
t.start();
//模擬5秒
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//5秒之后強行終止t執行緒
r.run=false;
}
}
class MyRunnable4 implements Runnable{
public boolean run=true;
public void run() {
//打一個bool標記
for(int i=0;i<10;i++){
if(run){
System.out.println(Thread.currentThread().getName()+"---->"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
//return就結束了,你在結束之前還有什么沒保存的,在這里保存就行了,
//終止當前執行緒的執行
return ;
}
}
}
}

4.執行緒調度
執行緒調度概述
關于執行緒的調度
1.1、常見的執行緒調度模型有哪些?
??搶占式調度模型:
??那個執行緒的優先級比較高,搶到的CPU時間片的概率就高一些/多一些,
??java采用的就是搶占式調度模型,
??均分式調度模型:
??平均分配CPU時間片,每個執行緒占有的CPU時間片時間長度一樣,
??平均分配,一切平等,
??有一些編程語言,執行緒調度模型采用的是這種方式,
1.2、java中提供了哪些方法是和執行緒調度有關系的呢?
實體方法:
??void setPriority(int newPriority) 設定執行緒的優先級
??int getPriority() 獲取執行緒優先級
??最低優先級1
??默認優先級是5
??最高優先級10
??優先級比較高的獲取CPU時間片可能會多一些,(但也不完全是,大概率是多的,)
靜態方法:
??static void yield() 讓位方法
??暫停當前正在執行的執行緒物件,并執行其他執行緒
??yield()方法不是阻塞方法,讓當前執行緒讓位,讓給其它執行緒使用,
??yield()方法的執行會讓當前執行緒從“運行狀態”回到“就緒狀態”,
??注意:在回到就緒之后,有可能還會再次搶到,
??void join()合并執行緒
class MyThread1 extends Thread {
public void doSome(){
MyThread2 t = new MyThread2();
t.join(); // 當前執行緒進入阻塞,t執行緒執行,直到t執行緒結束,當前執行緒才可以繼續,
}
}
class MyThread2 extends Thread{
}
執行緒優先級
public class ThreadTest10 {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("最高優先級"+Thread.MAX_PRIORITY);
System.out.println("最低優先級"+Thread.MIN_PRIORITY);
System.out.println("默認優先級"+Thread.NORM_PRIORITY);
//獲取當前執行緒物件,獲取當前執行緒的優先級
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName()+"執行緒的默認優先級是:"+currentThread.getPriority());
Thread t=new Thread(new MyRunnable5());
t.setName("t");
t.setPriority(8);
t.start();
//優先級較高的,只是搶到的CPU時間片相對多一些
//大概率方向更偏向于優先級較高的
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
class MyRunnable5 implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"執行緒的默認優先級是:"+Thread.currentThread().getPriority());
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
處于運行狀態的時間多一些


執行緒讓位
讓位:當前運行的執行緒暫停,會到就緒狀態,讓給其他執行緒
靜態方法:Thread.yield()
public class ThreadTest11 {
public static void main(String[] args) {
Thread t=new Thread(new MyRunnable6());
t.setName("t");
t.start();
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}
class MyRunnable6 implements Runnable{
public void run() {
//假設每100個讓位一次
for(int i=0;i<1000;i++){
if(i%100==0){
Thread.yield();//當前執行緒暫停一下,讓給主執行緒
}
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}

執行緒合并
public class ThreadTest12 {
public static void main(String[] args) {
System.out.println("main begin");
Thread t=new Thread(new MyRunnable7());
t.setName("t");
t.start();
//合并執行緒
try {
t.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //t合并到當前執行緒中,當前執行緒受阻塞,t執行緒執行直到結束,
System.out.println("main over");
}
}
class MyRunnable7 implements Runnable{
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println(Thread.currentThread().getName()+"---->"+i);
}
}
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/241997.html
標籤:java
下一篇:初學Java
