線程的狀態
準確來說,應該有五個狀態。
被創建後通過start()進入到運行狀態。執行完run()方法或者調用stop()方法,會從運行狀態進入到消亡狀態。而運行狀態,又會隨時因爲CPU的切換,進入到臨時阻塞狀態,或者叫“掛起”。如果在運行時期,調用sleep()或者wait()方法,會使線程進入凍結狀態。當休眠結束或者調用notify()方法,會讓線程回到運行狀態,或者是臨時阻塞狀態。
多線程的創建
有兩種方式,第一種是繼承Thread類,第二種是實現Runnable接口。
繼承Thread類
第一種方式繼承Thread類,將需要多線程處理的業務放到Thread類的run方法中。直接創建繼承後的類對象,使用start()方法,即可開啓多線程。
class MultiThreadDemo extends Thread{
private String name;
MultiThreadDemo(String name){
this.name=name;
}
@Override
public void run() {
for(int x=0;x<100;x++){
System.out.println("任務:"+name+"...執行第"+x+"次...Thread-name:"+Thread.currentThread().getName());
}
}
}
public class MulitThreadTest {
public static void main(String[] args) {
MultiThreadDemo mtd1=new MultiThreadDemo("gg");
MultiThreadDemo mtd2=new MultiThreadDemo("哈哈");
mtd1.start();
mtd2.start();
}
}
這種方式的弊端在於:①獲得了Thread中不需要的方法。②僅僅因爲需要開啓多線程,就繼承了Thread了,成爲了Thread類體系,而這個類本來跟Thread類毫不相干。③一旦繼承了Thread類,就不能繼承其它類了。
實現Runnable接口
第二種方式實現Runnable接口,覆寫run方法。在調用的時候,把實現了該接口的類作爲參數,傳到Thread類的構造函數中,最後調用Thread對象的start()方法即可。
class MultiThreadDemo implements Runnable{
private String name;
MultiThreadDemo(String name){
this.name=name;
}
@Override
public void run() {
for(int x=0;x<100;x++){
System.out.println("任務:"+name+"...執行第"+x+"次...Thread-name:"+Thread.currentThread().getName());
}
}
}
public class MulitThreadTest {
public static void main(String[] args) {
MultiThreadDemo mtd1=new MultiThreadDemo("哈哈");
MultiThreadDemo mtd2=new MultiThreadDemo("gg");
Thread t1=new Thread(mtd1);
Thread t2=new Thread(mtd2);
t1.start();
t2.start();
}
}
這種方式的優點在於:①避免了單繼承的侷限。②直接將具體任務封裝成了具體的線程對象。
賣票示例
四個機器(線程)同時賣100張票。下面這種使用繼承Thread的方式,創建四個Ticket對象,肯定是不行的,各賣各的。
Ticket ticket1=new Ticket();
Ticket ticket2=new Ticket();
Ticket ticket3=new Ticket();
Ticket ticket4=new Ticket();
ticket1.start();
ticket2.start();
ticket3.start();
ticket4.start();
票是公有的,要被所有線程共享,一種方式是將票數變成靜態的。另外一種是實現Runnable接口,四個線程用同一個對象。
class Ticket implements Runnable{
private int num=100;
@Override
public void run() {
while(true){
if(num>0){
System.out.println(Thread.currentThread().getName()+"....sale..."+num--);
}
}
}
}
public class TicektSell {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread t1=new Thread(ticket);
Thread t2=new Thread(ticket);
Thread t3=new Thread(ticket);
Thread t4=new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
這樣,似乎就實現了四個機器同時賣100張票。
線程安全問題
上面的代碼,如果在if判斷裏面加上一條線程睡眠,就會出現“線程安全”問題。
if(num>0){
try {
Thread.sleep(100);
}catch (InterruptedException e){e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"....sale..."+num--);
}
票賣到了0,還在繼續賣,甚至出現了負數!
原因在於:假設此時票數=1,線程0進行if判斷,爲true,進入到if代碼塊。這個時候,突然CPU切換線程,線程0被掛起。此時輪到線程1進行if判斷,票數還是1,依然可以進入到if代碼塊。這個時候,CPU再次切換,線程1也被掛起,輪到線程2進行if判斷,還是可以進去。
接下來,CPU切回線程0,線程0將票數-1,此時票數爲0。再切回線程1,線程1繼續-1,票數變成了-1。最後切回線程2,再-1,變成了-2。這樣就出現了線程安全問題。
對於線程安全問題,做法就是加鎖,我這裏沒處理完,你就不要進來。