看了個視頻,嗯,記錄下加深下理解。。。java實現多線程的兩種方式:繼承thread類,這種方式的缺陷就是java的單繼承導致的;還有一種方式就是實現Runnable介面,這種方式避免了thread方式由於java單繼承帶來的缺陷。需要知道的是線程啟動後會搶佔CPU資源,如果當前new了一個線程,線程就會進入建立的狀態,然後我們啟動這個線程,thread.start(),這個時候這個線程就會處在就緒狀態,如果當前能擷取到CPU資源,然後就會處在運行狀態,如果這個時候執行了sleep方法或者一些阻塞事件,會導致線程處於阻塞狀態,然後sleep到一定時間後阻塞解除,會繼續排隊到就緒狀態,繼續等待當前釋放CPU釋放資源,如果run方法執行完畢的話,那麼線程終止,自動銷毀。這也是為什麼不加synchronized的話會出現亂序的情況。因為你雖然進入了線程隊列,但是當前cpu資源不一定是被你獲得了,會被後來居上。也就是說你可能是先start()了,但是不一定能先run()。
使用繼承thread的方式進行類比賣票
public class SoldTicketThread extends Thread {
private int ticket = 5;
private String name;
// 構造方法
public SoldTicketThread(String name) {
this.name = name;
}
public void run() {
while (ticket > 0) {
System.out.println(name + "賣出了一張票,剩餘" + (--ticket) + "張票");
}
}
public static void main(String[] args) {
// 建立三個線程,類比三個視窗賣票
SoldTicketThread wd1 = new SoldTicketThread("視窗一");
SoldTicketThread wd2 = new SoldTicketThread("視窗二");
SoldTicketThread wd3 = new SoldTicketThread("視窗三");
// 啟動者三個線程,也就是視窗開始賣票
wd1.start();
wd2.start();
wd3.start();
}
}
運行結果:(這是因為我們建立了3個線程,每個線程都有5張票的資源,所以每個線程都賣自己的5張票,無法實現資源共用)
視窗二賣出了一張票,剩餘4張票
視窗一賣出了一張票,剩餘4張票
視窗一賣出了一張票,剩餘3張票
視窗一賣出了一張票,剩餘2張票
視窗一賣出了一張票,剩餘1張票
視窗一賣出了一張票,剩餘0張票
視窗二賣出了一張票,剩餘3張票
視窗二賣出了一張票,剩餘2張票
視窗二賣出了一張票,剩餘1張票
視窗二賣出了一張票,剩餘0張票
視窗三賣出了一張票,剩餘4張票
視窗三賣出了一張票,剩餘3張票
視窗三賣出了一張票,剩餘2張票
視窗三賣出了一張票,剩餘1張票
視窗三賣出了一張票,剩餘0張票
綜合上面的結果,我們只能建立一個線程,拿到這5張票的共用資源
public class SoldTicketThread implements Runnable {
private int ticket = 5;
public void run() {
while (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩餘" + (--ticket) + "張票");
}
}
public static void main(String[] args) {
// 建立一個實現了runnable介面的對象,拿到這5張票的資源
SoldTicketThread window = new SoldTicketThread();
//將這個資源類作為參數傳遞到3個線程中
Thread wd1=new Thread(window,"視窗一");
Thread wd2=new Thread(window,"視窗二");
Thread wd3=new Thread(window,"視窗三");
// 啟動者三個線程,也就是視窗開始賣票
wd1.start();
wd2.start();
wd3.start();
}
}
運行結果:
視窗一賣出了一張票,剩餘4張票
視窗一賣出了一張票,剩餘2張票
視窗一賣出了一張票,剩餘1張票
視窗二賣出了一張票,剩餘3張票
視窗一賣出了一張票,剩餘0張票
如果你非要用繼承thread的方式的話。。。
public class SoldTicketThread extends Thread {
private int ticket = 5;
public void run() {
while (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩餘"
+ (--ticket) + "張票");
}
}
public static void main(String[] args) {
SoldTicketThread window = new SoldTicketThread();
Thread wd1 = new Thread(window, "視窗二");
Thread wd2 = new Thread(window, "視窗一");
Thread wd3 = new Thread(window, "視窗三");
wd1.start();
wd2.start();
wd3.start();
}
}
運行結果:視窗隨機,賣票順序隨機
視窗二賣出了一張票,剩餘4張票
視窗二賣出了一張票,剩餘1張票
視窗二賣出了一張票,剩餘0張票
視窗三賣出了一張票,剩餘2張票
視窗一賣出了一張票,剩餘3張票
好想讓這個賣票有個順序腫麼破。。。,使用synchronized鎖住賣票操作的整個內容,使一次只賣一次票。給我們的資源加把鎖
採用實現Runnable介面實現:
public class SoldTicketThread implements Runnable {
private int ticket = 5;
private synchronized void sale() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩餘"+ (--ticket) + "張票");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void run() {
while (ticket > 0) {
sale();
// 這裡要確保進入賣票環節前就把資源鎖住
}
}
public static void main(String[] args) {
SoldTicketThread window = new SoldTicketThread();
Thread wd1 = new Thread(window, "視窗二");
Thread wd2 = new Thread(window, "視窗一");
Thread wd3 = new Thread(window, "視窗三");
wd1.start();
wd2.start();
wd3.start();
}
}
運行結果:(線程間(視窗)會進行搶佔cpu資源,是隨機的)
視窗三賣出了一張票,剩餘4張票
視窗一賣出了一張票,剩餘3張票
視窗二賣出了一張票,剩餘2張票
視窗一賣出了一張票,剩餘1張票
視窗一賣出了一張票,剩餘0張票
繼承Thread類
public class SoldTicketThread extends Thread {
private int ticket = 5;
public synchronized void sale() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣出了一張票,剩餘"
+ (--ticket) + "張票");
}
}
public void run() {
while (ticket > 0) {
sale();
}
}
public static void main(String[] args) {
SoldTicketThread window = new SoldTicketThread();
Thread wd1 = new Thread(window, "視窗二");
Thread wd2 = new Thread(window, "視窗一");
Thread wd3 = new Thread(window, "視窗三");
wd1.start();
wd2.start();
wd3.start();
}
}
運行結果:(視窗會進行搶佔cpu資源,是隨機的)
視窗三賣出了一張票,剩餘4張票
視窗一賣出了一張票,剩餘3張票
視窗一賣出了一張票,剩餘2張票
視窗一賣出了一張票,剩餘1張票
視窗一賣出了一張票,剩餘0張票
總結:因為我們只是建立了一個擁有5張票的線程類的對象(有5張火車票),然後把這個對象作為參數傳遞給3個新建立的thread對象,所以共用了這5張火車票。然後我們再賣票操作加了synchronized操作,就可以實現按順序賣票了。
需要記住的點是,我覺得初學者老師會覺得程式是從上到下執行的,很符合我們的邏輯,但是線程可以看做是並行作業,並不是說你先start了,你就會先run。
還要記錄的點事守護線程的點。。。在Dos環境下,jstack可以查看當前運行java類的pid,然後輸入 jstack -l pid可以查看到目前有什麼守護線程。。。。