感性地理解一下什麼是線程?java
線程這個概念實際上是比較抽象的,雖然依照教科書上的說法:多線程
進程是從系統獲取資源的最小單位,線程是程序執行的最小單位。程序是靜態存在於磁盤上的一段文本,進程運行這段文本記錄的命令。ide
也就是說,進程從系統那裏獲取到了必定的CPU佔用時間片、內存單元和IO等等資源,而後線程將這些資源利用起來執行程序,線程執行程序是什麼意思呢?就是把程序記錄的那些命令逐條依序一步步在CPU上運做,數據在內存、IO上流轉,將命令執行完。
這個層級的概念存在於OS上,OS的調度抽象層級並非那麼直觀,若是咱們在說明白一點,在作底層的計算機組成原理實驗的時候,在咱們接好連線後硬件就具備了處理數據的能力,只要扳動不一樣的開關就能夠將數據讀寫在不一樣的芯片上,咱們的程序也許是爲了完成數據流轉寫在紙上的扳動不一樣開關的序列,因此程序是屬於IO級別的,而後咱們依照紙上的命令序列實際上手去扳動不一樣的開關執行的就是這段程序,因此咱們本身充當的角色就是進程,最終就得出了這樣的結論:進程「執行」程序。至於線程呢,能夠看做是進程在執行過程當中的策略,好比說在一我的扳動開關的時候就是單進程單線程,若是是兩我的扳動開關就是單進程多線程,若是兩我的能配合起來扳動開關就是多線程同步,因此線程和進程之間並不是互斥的概念,而是相容的概念,若是有線程就必定有進程,一個進程包含了至少一個的線程。函數
建立線程的方法
1.建立直接建立Thread的子類,重寫run()方法;測試
class MyThread extends Thread { @Override public void run() { System.out.println("This is my thread"); } } public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }
2.建立一個線程執行類實現Runnable接口,在這個執行類裏實現Runnable的run()方法,建立該執行類的對象後,用此執行類對象初始化新線程,啓動新線程時即執行這個執行對象的run()方法;線程
class MyRunnable implements Runnable { @Override public void run() { System.out.println("This is my thread"); } } public class Test { public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); } }
3.經過線程工廠用工廠模式來建立新線程,新建工廠類繼承ThreadFactory類重寫newThread()方法,經過指定實現了Runnable接口的執行類來建立與之對應的線程;code
public class ThreadFactoryDemo { public static void main(String[] args) { ThreadFactory factory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { // TODO Auto-generated method stub return new Thread(r); } }; factory.newThread(new Runnable() { @Override public void run() { System.out.println("in runnable."); } }).start(); } }
注意:只有調用Thread類的Start方法,才能真正地在一個獨立的線程中執行代碼,直接調用Thread類的run方法,並不能啓動一個新的線程,代碼是在調用者線程中執行的。對象
那麼主線程的run()方法在哪裏呢?任何java程序的main執行入口擔當着啓動主線程的做用,只要進入了main函數就執行了主線程,所以整個main函數裏的內容就是主線程的run()方法。繼承
線程究竟執行哪一個run()方法
當線程同時具備可執行對象實現的run()方法和線程重寫的run()方法時,啓動線程時究竟執行哪一個run()方法呢?
結果是若是隻定義了可執行對象的run()方法則執行這個run()方法,若是隻重寫了線程的run()方法則執行這個run()方法,若是兩個方法都有則執行線程重寫的run()方法。接口
public class Test { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Runnable.run()"); } }) { @Override public void run() { // TODO Auto-generated method stub System.out.println("Thread.run()"); } }; thread.start(); } }
線程的休眠
使用Thread類的sleep()方法或者使用TimeUnit的相關方法來休眠線程,休眠的意思是資源仍被佔用,可是線程保留原來的狀態沒有活動;
public class ThreadSleep { public static void main(String[] args) { Thread th = new Thread(new Runnable() { public void run() { for (int i = 0; i < 10; i++) { try { // Thread.sleep(500); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } }); th.start(); } }
線程中斷
線程中斷的意思是線程中止當前的運行狀態讓出資源結束生命週期,當外界想要一個線程中斷時須要調用它的interrupted()方法,調用後不是直接就能夠中斷這個線程,而是將線程的interrupted標記位賦爲1,若是要線程要響應這個中斷則按期須要檢查這個標記,檢查到被中斷標記後本身退出執行狀態。
public class ThreadInterruptDemo { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { while (true) { System.out.println("running"); if (isInterrupted()) return; } } }; thread.start(); Thread.sleep(2000); thread.interrupt(); } }
線程定時任務
線程要實現定時任務的話可使用Runnable的實現類TimerTask,此類須要重寫run()方法以完成具體須要進行的定時任務。而後由定時器Timer來調度,使用Timer的schedle()方法至關於啓動這個定時任務線程。
public class TimerTaskDemo { public static void main(String[] args) { TimerTask task = new TimerTask() { private int counter = 0; @Override public void run() { System.out.println(counter + ":invoked!"); counter++; } }; Timer timer = new Timer(); // 過2秒鐘後首次運行,之後每隔3秒運行一次 timer.schedule(task, 2000, 3000); } }
線程運行過程當中的異常處理
線程的run()方法中是不容許直接拋出異常的,也就是說不能有這樣的寫法:run() throws Exception ,緣由在於在線程的運行過程當中應該最大限度地保持正常工做,所以除了一些不可預知的運行時異常,不該該主動拋出受控異常。若是非要在run()方法裏處理拋出的異常,則應該定義一個實現了UncaughtExceptionHandler的類,而後指定這個類的對象在重寫的uncaughtException()方法裏去處理拋出的異常。另一種方法是,將這個線程加入一個線程組,在線程組裏重寫uncaughtException()方法來處理拋出的異常,這時線程組的做用至關於實現了UncaughtExceptionHandler的類。
1.使用handler對象處理異常:
public class ThreadTest { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { throw new RuntimeException("格式錯誤"); } }); thread.setUncaughtExceptionHandler(new MyHandler()); thread.start(); } } class MyHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println(t.getName() + ":" + e.getMessage()); } }
2.使用線程組處理異常:
public class ThreadGroupDemo { public static void main(String[] args) { ThreadGroup threadGroup1 = new ThreadGroup("group1") { public void uncaughtException(Thread t, Throwable e) { System.out.println(t.getName() + ": " + e.getMessage()); } }; Thread thread1 = new Thread(threadGroup1, new Runnable() { public void run() { throw new RuntimeException("測試異常"); } }); thread1.start(); } }