簡單的說,單線程就是進程中只有一個線程。單線程在程序執行時,所走的程序路徑按照連續順序排下來,前面的必須處理好,後面的纔會執行。java
Java示例:網絡
public class SingleThread { public static void main(String[] args) { for (int i = 0; i < 10000; i++) { System.out.print(i + " "); } } }
上述Java代碼中,只有一個主線程執行main
方法。多線程
由一個以上線程組成的程序稱爲多線程程序。常見的多線程程序如:GUI應用程序、I/O操做、網絡容器等。
Java中,必定是從主線程開始執行(main方法),而後在主線程的某個位置啓動新的線程。架構
Java中建立多線程類兩種方法:併發
一、繼承java.lang.Threadide
Java示例:this
public class MyThread extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.print(i + " "); } } } public class MultiThread { public static void main(String[] args) { MyThread t = new MyThread(); t.start(); //啓動子線程 //主線程繼續同時向下執行 for (int i = 0; i < 10000; i++) { System.out.print(i + " "); } } }
上述代碼中,MyThread
類繼承了類java.lang.Thread
,並覆寫了run
方法。主線程從main
方法開始執行,當主線程執行至t.start()
時,啓動新線程(注意此處是調用start
方法,不是run
方法),新線程會併發執行自身的run
方法。spa
二、實現java.lang.Runnable接口線程
Java示例:3d
public class MyThread implements Runnable { public void run() { for (int i = 0; i < 10000; i++) { System.out.print(i + " "); } } } public class MultiThread { public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.start(); //啓動子線程 //主線程繼續同時向下執行 for (int i = 0; i < 10000; i++) { System.out.print(i + " "); } } }
上述代碼中,MyThread
類實現了java.lang.Runnable
接口,並覆寫了run
方法,其它與繼承java.lang.Thread
徹底相同。實際上,java.lang.Thread
類自己也實現了Runnable
接口,只不過java.lang.Thread
類的run方法主體裏空的,一般被子類覆寫(override
)。
注意:主線程執行完成後,若是還有子線程正在執行,程序也不會結束。只有當全部線程都結束時(不含Daemon Thread),程序纔會結束。
Java中線程的暫停是調用java.lang.Thread
類的sleep
方法(注意是類方法)。該方法會使當前正在執行的線程暫停指定的時間,若是線程持有鎖,sleep
方法結束前並不會釋放該鎖。
Java示例:
public class Main { public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.print(i + " "); try { Thread.sleep(1000); //當前main線程暫停1000ms } catch (InterruptedException e) { } } } }
上述代碼中,當main線程調用Thread.sleep(1000)
後,線程會被暫停,若是被interrupt
,則會拋出InterruptedException
異常。
Java中線程的共享互斥操做,會使用synchronized關鍵字。線程共享互斥的架構稱爲監視(monitor),而獲取鎖有時也稱爲「持有(own)監視」。
每一個鎖在同一時刻,只能由一個線程持有。
注意:synchronized
方法或聲明執行期間,如程序遇到任何異常或return,線程都會釋放鎖。
一、synchronized方法
Java示例1:
//synchronized實例方法 public synchronized void deposit(int m) { System.out.print("This is synchronized method."); }
注:synchronized實例方法採用this鎖(即當前對象)去作線程的共享互斥。
Java示例2:
//synchronized類方法 public static synchronized void deposit(int m) { System.out.print("This is synchronized static method."); }
注:synchronized類方法採用類對象鎖(即當前類的類對象)去作線程的共享互斥。如上述示例中,採用類.class(繼承自java.lang.Class)做爲鎖。
二、synchronized聲明
Java示例:
public void deposit(int m) { synchronized (this) { System.out.print("This is synchronized statement with this lock."); } synchronized (Something.class) { System.out.print("This is synchronized statement with class lock."); } }
注:synchronized聲明能夠採用任意鎖,上述示例中,分別採用了對象鎖(this)和類鎖(something.class)
java.lang.Thread類有一個interrupt
方法,該方法直接對線程調用。當被interrupt的線程正在sleep或wait時,會拋出InterruptedException
異常。
事實上,interrupt
方法只是改變目標線程的中斷狀態(interrupt status),而那些會拋出InterruptedException
異常的方法,如wait、sleep、join等,都是在方法內部不斷地檢查中斷狀態的值。
Thread實例方法:必須由其它線程獲取被調用線程的實例後,進行調用。實際上,只是改變了被調用線程的內部中斷狀態;
Thread類方法:必須在當前執行線程內調用,該方法返回當前線程的內部中斷狀態,而後清除中斷狀態(置爲false) ;
Thread實例方法:用來檢查指定線程的中斷狀態。當線程爲中斷狀態時,會返回true;不然返回false。
一、wait set / wait方法
wait set是一個虛擬的概念,每一個Java類的實例都有一個wait set,當對象執行wait方法時,當前線程就會暫停,並進入該對象的wait set。
當發生如下事件時,線程纔會退出wait set:
①有其它線程以notify方法喚醒該線程
②有其它線程以notifyAll方法喚醒該線程
③有其它線程以interrupt方法喚醒該線程
④wait方法已到期
注:當前線程若要執行obj.wait()
,則必須先獲取該對象鎖。當線程進入wait set後,就已經釋放了該對象鎖。
下圖中線程A先得到對象鎖,而後調用wait()方法(此時線程B沒法獲取鎖,只能等待)。當線程A調用完wait()方法進入wait set後會自動釋放鎖,線程B得到鎖。
二、notify方法
notify方法至關於從wait set中從挑出一個線程並喚醒。
下圖中線程A在當前實例對象的wait set中等待,此時線程B必須拿到同一實例的對象鎖,才能調用notify方法喚醒wait set中的任意一個線程。
注:線程B調用notify方法後,並不會當即釋放鎖,會有一段時間差。
三、notifyAll方法
notifyAll方法至關於將wait set中的全部線程都喚醒。
四、總結
wait、notify、notifyAll這三個方法都是java.lang.Object類的方法(注意,不是Thread類的方法)。
若線程沒有拿到當前對象鎖就直接調用對象的這些方法,都會拋出java.lang.IllegalMonitorStateException異常。
obj.wait()
是把當前線程放到obj的wait set;obj.notify()
是從obj的wait set裏喚醒1個線程;obj.notifyAll()
是喚醒全部在obj的wait set裏的線程。