併發:指兩個或多個事件在同一時間段內發生。
並行:指兩個或多個事件在同一時刻發生。
進程:指一個內存中運行的應用程序,每個進程都有獨立的內存空間,一個應用程序可以運行多個進程。
線程:線程是進程中的一個執行單元。
小結:
線程的作用是提供一個輕量級執行單元——雖比進程小,但仍能執行任何java代碼。一般情況下,對操作系統來說,一個線程是一個完成的執行單元,但仍屬於一個進程,進程的地址空間在組成該進程的所有線程之間共享。也就是說,每個線程都可以獨立調度,而且有自己的棧和程序計數器,但會和同個進程中的其他線程共享內存和對象。
線程調度
- 分時調度:所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間
- 搶佔式調度:優先級高的線程使用CPU,如果線程的優先級相同,則隨機選擇一個(線程隨機性),java的使用爲搶佔式調度
創建線程
創建多線程程序的第一種方式:
創建Thread類的子類
java.lang.Thread類:是描述線程的類,我們想要實現多線程程序,就必須繼承Thread類
實現步驟:
1.創建一個Thread類的子類
2.在Thread類中的子類重寫run方法
3.創建Thread類的子類對象
4.調用Thread類中的方法start,開啓線程,執行run方法
(多次啓動一個線程是非法的,特別是當線程已經結束後,不能再重新啓動)
(java的線程使用爲搶佔式調度)
package demo1;
public class MyThread01 extends Thread{
public void run() {
for(int i=0;i<5;i++) {
System.out.println("thread:"+i);
}
}
}
package demo1;
public class myDemo12 {
public static void main(String[] args) throws Exception {
MyThread01 myThread = new MyThread01();
myThread.start(); //開闢新的棧空間,執行run方法
for(int i=5;i<10;i++) {
System.out.println("main:"+i);
}
}
}
運行結果:
main:5
thread:0
main:6
thread:1
main:7
thread:2
main:8
thread:3
main:9
thread:4
或者是:
使用匿名內部類
Thread thread = new Thread() {
public void run() {
for(int i=5;i<10;i++) {
System.out.println("main:"+i);
}
}
};
thread.start();
Thread常用方法
setName(Sting name); //設置線程的名字
getName(); //獲取線程的名字
sleep(long ms); //暫定執行線程
join(); //讓此線程強制執行,其他線程無法運行,必須等待此線程完成之後纔可以繼續執行
interrupt(); //當一個線程運行時,另外一個線程可以直接通過interrupt()方法中斷其運行狀態
setPriority(Thread.MIN_PRIORITY); //設置線程的優先級爲最低(NORM_PRIORITY中等,MAX_PRIORITY最高)
【注意】並非優先級越高就一定會先執行,哪個線程先執行將由 CPU 的調度決定
yield(); //線程禮讓,將一個線程的操作暫時讓給其他線程執行
wait(); //線程等待,屬於Object類,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 喚醒方法
創建多線程程序的第二種方式:
實現Runnable接口
實現步驟:
1.創建一個Runnable接口的實現類
2.在實現類中重寫Runnable接口的run方法
3.創建一個Runnable接口的實現類對象
4.創建Thread類對象,構造方法中傳遞Runnable接口的實現類對象
5.調用Thread類對象的start方法
package demo1;
public class ImpRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println("thread:"+i);
}
}
}
package demo1;
public class myDemo12 {
public static void main(String[] args) throws Exception {
ImpRunnable imp = new ImpRunnable();
Thread run = new Thread(imp);
run.start();
}
}
或者使用匿名內部類
new Thread(new Runnable() {
public void run() {
for(int i=5;i<10;i++) {
System.out.println("main:"+i);
}
}
}).start();
或者用Lambda表達式替代匿名內部類
new Thread(()->{
for(int i=5;i<10;i++){
System.out.println("main:"+i);
}
}).start();
使用Runnable接口的好處:
1.避免了單繼承的侷限性
2.增強程序的擴展性,降低了程序的耦合性(解耦)
(設置線程任務與開啓線程進行隔離)
提示:
可以通過重載線程類的構造函數來傳入參數,以便run方法使用
-
同步與死鎖
參考:https://www.cnblogs.com/java1024/archive/2019/11/28/11950129.html
一個多線程的程序如果是通過 Runnable 接口實現的,則意味着類中的屬性被多個線程共享,那麼這樣就會造成一種問題,如果這多個線程要操作同一個資源時就有可能出現資源同步問題。
解決方法:
同步代碼塊
synchronized(同步對象){
需要同步的代碼
}
class MyThread implements Runnable{
private int ticket = 5 ; // 假設一共有5張票
public void run(){
for(int i=0;i<100;i++){
synchronized(this){ // 要對當前對象進行同步
if(ticket>0){ // 還有票
try{
Thread.sleep(300) ; // 加入延遲
}catch(InterruptedException e){
e.printStackTrace() ;
}
System.out.println("賣票:ticket = " + ticket-- );
}
}
}
}
};
public class SyncDemo02{
public static void main(String args[]){
MyThread mt = new MyThread() ; // 定義線程對象
Thread t1 = new Thread(mt) ; // 定義Thread對象
Thread t2 = new Thread(mt) ; // 定義Thread對象
Thread t3 = new Thread(mt) ; // 定義Thread對象
t1.start() ;
t2.start() ;
t3.start() ;
}
};
運行結果:
賣票:ticket = 5;
賣票:ticket = 4;
賣票:ticket = 3;
賣票:ticket = 2;
賣票:ticket = 1;
同步方法
除了可以將需要的代碼設置成同步代碼塊外,也可以使用 synchronized 關鍵字將一個方法聲明爲同步方法。
synchronized 方法返回值 方法名稱(參數列表){
}
class MyThread implements Runnable{
private int ticket = 5 ; // 假設一共有5張票
public void run(){
for(int i=0;i<100;i++){
this.sale() ; // 調用同步方法
}
}
public synchronized void sale(){ // 聲明同步方法
if(ticket>0){ // 還有票
try{
Thread.sleep(300) ; // 加入延遲
}catch(InterruptedException e){
e.printStackTrace() ;
}
System.out.println("賣票:ticket = " + ticket-- );
}
}
};
public class SyncDemo03{
public static void main(String args[]){
MyThread mt = new MyThread() ; // 定義線程對象
Thread t1 = new Thread(mt) ; // 定義Thread對象
Thread t2 = new Thread(mt) ; // 定義Thread對象
Thread t3 = new Thread(mt) ; // 定義Thread對象
t1.start() ;
t2.start() ;
t3.start() ;
}
};
運行結果:
賣票:ticket = 5;
賣票:ticket = 4;
賣票:ticket = 3;
賣票:ticket = 2;
賣票:ticket = 1;
死鎖
所謂死鎖,就是兩個線程都在等待對方先完成,造成程序的停滯,一般程序的死鎖都是在程序運行時出現的。
參考:java技術手冊