基本概念
基本:
- 程序:一段靜態代碼,靜態對象
- 進程:程序的一次執行過程
- 線程:進程內部一個執行路徑
比如:QQ正在運行是一個進程,同時聊天,視頻 兩個線程
何時需要多線程:
- 程序需要執行多個任務
- 執行等待的任務:如搜索時需要等待服務器返回響應,等待時間讓CPU做別的事情,先讓這個程序佔有的CPU做別的事情
- 後臺運行程序:在主程序的執行過程中,中途有一行代碼開啓了線程,當其中的主程序運行的代碼和線程代碼並行執行,則主程序代碼和線程代碼就不想關了
多線程的建立與啓動
thread類(1)的:
- 特性run() :線程體:代碼寫在run()方法中
- start():用來啓動線程
- 異步性:
- 兩個線程各自保持前進的速度,你執行一次,我執行一次,接着我執行或者你執行,按照不同的順序推進
創建線程的兩種方式:
繼承Thread類:
-
創建一個子類繼承thread類
-
子類重寫run方法
-
創建子類對象:即創建了線程對象
-
調用對象的start方法
public class Test {
public static void main(String[] args) {
//3步:創建多線程對象
Thread t=new ThreadTest();
t.start();
//4步:調用start方法,啓動線程,調用run方法
//threadTest.start();System.out.println("=========="); System.out.println("=======5==="); System.out.println("==========");
}
}/**創建一個子類繼承thread類
- 多線程:繼承thread創建多線程
- @author douer
*/
public class ThreadTest extends Thread{
//1.重寫thread
@Override
public void run() {
System.out.println(“多線程運行代碼”);
for(int i=0;i<3;i++) {
System.out.println(i+6);
System.out.println(i+6);
}
}
}
實現接口Runnable:
-
定義子類實現runnable接口
子類重寫run方法
public class RunableTest implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" : 多線程運行代碼"); -
在test中通過thread類含餐構造創建線程方法
Thread t2=new Thread(new RunableTest(), “t-2 :”);//參數1是new一個實例,參數2是線程的名字 -
start開啓線程
-
Thread.currentThread().getName():獲取當前線程的名稱
-
線程名稱的作用:多線程運行時,可以明確是具體哪個的線程在運行
實現接口:
package ThreadTest;
/**
* 實現runnable接口的方式創建多線程
* @author douer
*
*/
public class RunableTest implements Runnable {@Override public void run() { System.out.println(Thread.currentThread().getName()+" : 多線程運行代碼"); for(int i=0;i<3;i++) { System.out.println(Thread.currentThread().getName()+"==="+i); } } }
Test運行:
package ThreadTest;public class Test { public static void main(String[] args) { //第二種Runnable方 //2步:通過thread Thread t2=new Thread(new RunableTest(), "t-2 :");//參數1是new一個實例,參數2是線程的名字 t2.start(); //作用:多線程運行時,可以明確是具體哪個的線程在運行 Thread t3=new Thread(new RunableTest(), "t-3 :"); t3.start(); System.out.println("=======1=="); System.out.println("=======5==="); System.out.println("=======7=="); } }
兩者聯繫and區別:
-
區別:
-
常用接口方式來實現多線程:好處:
- 避免單繼承的侷限:‘
子類可以實現多個接口,但是繼承只能繼承一個。比如student同時實現多線程,同時,又實現person的接口 - 共享同一個接口的對象==從而共同處理一份資源
ex:-
兩個線程共同增加count 數值
-
兩個線程共享同一個對象
/new 同一個對象
RunableTest r=new RunableTest();
//參數r,被兩個線程共享
Thread t2=new Thread(r, “t-2 :”);
t2.start();
//作用:多線程運行時,可以明確是具體哪個的線程在運行
Thread t3=new Thread(r, “t-3 :”);
t3.start();
兩個線程同時對count進行++
public class RunableTest implements Runnable {int count=0; @Override public void run() { //System.out.println(Thread.currentThread().getName()+" : 多線程運行代碼"+count); for(int i=0;i<3;i++) { //System.out.println(Thread.currentThread().getName()+"==="+i); count++; System.out.println(Thread.currentThread().getName()+" : 多線程運行代碼"+count); } }
}
-
- 避免單繼承的侷限:‘
多線程優點
- 提高應用程序的響應
- CPU的利用率
- 改善程序結構:一個程序可以分成多個線程並行運行
thread類(2)的方法
-
join()阻塞:
專業說法:阻塞當前的main方法,先不執行System.out.println("=====5=");
先執行jion進來的代碼,jion的線程執行全部完畢之後繼續執行之前main阻塞的代碼
RunableTest r=new RunableTest();
Thread t2=new Thread(r, “t-2 :”);
t2.start();
Thread t3=new Thread(r, “t-3 :”);
t3.start();System.out.println("=======1=="); //把t2的run()方法添加到main中執行, //執行順序一定是t2中的run一定在1與5之間 t2.join(); System.out.println("=======5==="); System.out.println("=======7==");
-
yield():線程讓步
暫停現在執行的程序, -
sleep():睡眠
Thread.sleep(1000);
在實現線程的run中調用; 單位是毫秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} -
stop():強制停止線程聲明週期
RunableTest r=new RunableTest();
Thread t2=new Thread(r, “t-2 :”);
t2.start();
Thread t3=new Thread(r, “t-3 :”);
t3.start();System.out.println("=======1=="); System.out.println(t2.isAlive());//確認還活着 t2.stop();//在這句話之前t-2可以執行,之後就不能執行 System.out.println("=======5==="); t3.join(); System.out.println("=======7==");
t-2只執行了一次就消失了
-
boolean isAlive():判斷當前線程是否還存活
線程生命週期
- 新建:創建線程實例
- 就緒:執行了start()方法之後,進入隊列等待CPU的時間片
- 運行:run()方法開始執行
- 阻塞:run()被卡住
- 死亡:強制死亡stop();自然死亡;斷電;殺掉進程
線程死鎖
- 成因:不同的線程佔用需要的同步資源不放手,相互等待對方釋放自己需要的同步資源
- 解決方法:合理安排執行順序;如:加鎖順序一致
線程通信
-
wait():掛起線程==阻塞
-
notify():喚醒排隊中的最高級線程
-
notifyAll():喚醒所有線程
//案例:實現:支付寶永遠先執行,如果微信想先執行,則阻塞他
使用this:
public synchronized void drawing8(int m) throws Exception {
String name=Thread.currentThread().getName();if(name.equals("微信")) { this.wait(); //當前對象阻塞 } //取錢數目超過餘額 if(money<m) { System.out.println(name+"操作:賬戶金額不足,餘額爲:"+money); }else { System.out.println(name+"操作:賬戶原有金額:"+money); money-=m; System.out.println(name+"操作:賬戶提款金額:"+m); System.out.println(name+"操作:賬戶提款之後金額:"+money); } if(name.equals("支付寶")) { this.notify(); //喚醒當前最高優先級線程,處於就緒狀態 } }
生產者消費者問題
P V原理的實現
package ThreadTest;
/**
* 生產者消費者:
*
* @author douer
*
*/
/*
* 1.生產者生產的時候不消費,消費時不是生產
* 2. 產品數爲0開始生產,產品>5則停止生產
* 3.產品數==5則開始消費,直到全部消費完成
*/
public class Test4 {
public static void main(String[] args) {
Clerk c=new Clerk();
//生產者
//創建一個線程
//使用匿名類創建一個線程
new Thread(new Runnable() {
@Override
public void run() {
//加鎖
synchronized(c) {
//無限循環:跳出條件在裏面
while(true) {//考試時以爲這個沒用呢
//產品數爲0開始生產;直到到達一定數額
if(c.productNum==0) {
System.out.println("=====開始生產:"+c.productNum);
while(c.productNum<4) {
System.out.println("目前庫存:"+c.productNum++);
}
//生產完畢則跳出循環
System.out.println("====生產完畢::::"+c.productNum);
//生產完畢之後喚醒消費進程
c.notify();
}
//產品還有,不生產,進程阻塞
else {
try {
c.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}, "生產者").start();
//消費者
new Thread(new Runnable() {
@Override
public void run() {
//加鎖
synchronized(c) {
//無限循環:跳出條件在裏面
while(true) {
//產品數爲0開始生產;直到到達一定數額
if(c.productNum==4) {
System.out.println("=====開始消費哦:"+c.productNum);
while(c.productNum>0) {
System.out.println("目前庫存:"+c.productNum--);
}
//生產完畢則跳出循環
System.out.println("====消費完畢::::"+c.productNum);
c.notify();
}
//產品還有,不生產,進程阻塞
else {
try {
c.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}, "生產者").start();
//
// producer.start();
// consum.start();
}
}
class Clerk{
public static int productNum=0;
}