在操作系統中兩個比較容易混淆的概念是進程(process)和線程(thread)。操作系統中的進程是資源的組織單位。進程有一個包含了程序內容和數據的地址空間,以及其它的資源,包括打開的文件、子進程和信號處理器等。不同進程的地址空間是互相隔離的。而線程表示的是程序的執行流程,是CPU調度的基本單位。線程有自己的程序計數器、寄存器、棧和幀等。引入線程的動機在於操作系統中阻塞式I/O的存在。當一個線程所執行的I/O被阻塞的時候,同一進程中的其它線程可以使用CPU來進行計算。這樣的話,就提高了應用的執行效率。線程的概念在主流的操作系統和編程語言中都得到了支持。
一部分的Java程序是單線程的。程序的機器指令按照程序中給定的順序依次執行。Java語言提供了java.lang.Thread類來爲線程提供抽象。有兩種方式創建一個新的線程:一種是繼承java.lang.Thread類並覆寫其中的run()方法,另外一種則是在創建java.lang.Thread類的對象的時候,在構造函數中提供一個實現了java.lang.Runnable接口的類的對象。在得到了java.lang.Thread類的對象之後,通過調用其start()方法就可以啓動這個線程的執行。
一個線程被創建成功並啓動之後,可以處在不同的狀態中。這個線程可能正在佔用CPU時間運行;也可能處在就緒狀態,等待被調度執行;還可能阻塞在某個資源或是事件上。多個就緒狀態的線程會競爭CPU時間以獲得被執行的機會,而CPU則採用某種算法來調度線程的執行。不同線程的運行順序是不確定的,多線程程序中的邏輯不能依賴於CPU的調度算法。
Windows操作系統是支持多線程的,它可以同時執行很多個線程,也支持多進程,因此Windows操作系統是支持多線程多進程的操作系統。Linux和Uinux也是支持多線程和多進程的操作系統。DOS就不是支持多線程和多進程了,它只支持單進程,在同一個時間點只能有一個進程在執行,這就叫單線程。
CPU難道真的很神通廣大,能夠同時執行那麼多程序嗎?不是的,CPU的執行是這樣的:CPU的速度很快,一秒鐘可以算好幾億次,因此CPU把自己的時間分成一個個小時間片,我這個時間片執行你一會,下一個時間片執行他一會,再下一個時間片又執行其他人一會,雖然有幾十個線程,但一樣可以在很短的時間內把他們通通都執行一遍,但對我們人來說,CPU的執行速度太快了,因此看起來就像是在同時執行一樣,但實際上在一個時間點上。
那什麼纔是真正的多線程?如果你的機器是雙CPU,或者是雙核,這確確實實是多線程。
二、線程的創建和啓動
在JAVA裏面,JAVA的線程是通過java.lang.Thread類來實現的,每一個Thread對象代表一個新的線程。創建一個新線程出來有兩種方法:第一個是從Thread類繼承,另一個是實現接口runnable。VM啓動時會有一個由主方法(public static void main())所定義的線程,這個線程叫主線程。可以通過創建Thread的實例來創建新的線程。你只要new一個Thread對象,一個新的線程也就出現了。每個線程都是通過某個特定的Thread對象所對應的方法run()來完成其操作的,方法run()稱爲線程體。
Demo1:實現Runnable接口創建和啓動新線程- package com.liangdianshui;
- public class ThreadRunable {
- public static void main(String args[]) {
- MyRunable mRunable = new MyRunable();
- // r1.run();//這個稱爲方法調用,方法調用的執行是等run()方法執行完之後纔會繼續執行main()方法
- new Thread(mRunable).start(); //啓動一個線程
- for (int i = 0; i < 10; i++) {
- System.out.println("main:" + i);
- }
- }
- }
- /**
- * 自定義一個類,實現Runable接口 打印1-10
- *
- */
- class MyRunable implements Runnable {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println("MyRunable:" + i);
- }
- }
- }
在我的機子上運行的結果是這樣,在你們的機子上運行的結果也可能不一樣的!他的運行原理是這樣的:
- package com.liangdianshui;
- public class ThreadExtendThread {
- public static void main(String[] args) {
- MyThread mThread = new MyThread();
- mThread.start(); // 開啓一個新的線程
- for (int i = 0; i < 10; i++) {
- System.out.println("main:" + i);
- }
- }
- }
- /**
- * 繼承Thread
- *
- */
- class MyThread extends Thread {
- @Override
- public void run() {
- super.run();
- for (int i = 0; i < 10; i++) {
- System.out.println("MyThread: " + i);
- }
- }
- }
-
使用實現Runnable接口和繼承Thread類這兩種開闢新線程的方法,在選擇上:應該優先選擇實現Runnable接口這種方式去開闢一個新的線程。因爲接口的實現可以實現多個,而類的繼承只能是單繼承。因此在開闢新線程時能夠使用Runnable接口就儘量不要使用從Thread類繼承的方式來開闢新的線程。
通過上面的例子,我們可以這樣理解線程,主線程就是一個部門的主管,當遇到比較耗時的工作的時候,他可以安排部門的其他員工幫忙做,然後他自己繼續做下面的事情,最後只要知道員工完成的結果就行了~!
三、線程狀態轉換
3.1.線程控制的基本方法
3.2. sleep/join/yield方法介紹
Demo:
- package com.liangdianshui;
- import java.util.Date;
- public class ThreadRunable {
- public static void main(String args[]) {
- MyRunable mRunable = new MyRunable();
- // r1.run();//這個稱爲方法調用,方法調用的執行是等run()方法執行完之後纔會繼續執行main()方法
- new Thread(mRunable).start(); // 啓動一個線程
- for (int i = 0; i < 10; i++) {
- System.out.println("main:" + i);
- if (i == 5) {
- try {
- System.out.println(new Date().toLocaleString()); //打印當前時間
- Thread.sleep(5000); // 讓主線程休眠5秒
- System.out.println(new Date().toLocaleString()); //打印當前時間
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
- /**
- * 自定義一個類,實現Runable接口 打印1-10
- *
- */
- class MyRunable implements Runnable {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println("MyRunable:" + i);
- }
- }
- }
運行結果:
Demo:
- package com.liangdianshui;
- import java.util.Date;
- public class ThreadRunable {
- public static void main(String args[]) {
- MyRunable mRunable = new MyRunable();
- // r1.run();//這個稱爲方法調用,方法調用的執行是等run()方法執行完之後纔會繼續執行main()方法
- Thread mThread = new Thread(mRunable);
- mThread.start(); // 啓動一個線程
- for (int i = 0; i < 10; i++) {
- System.out.println("main:" + i);
- }
- try {
- mThread.join(); //把子線程合併到主線程,等於是調用子線程的方法
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- /**
- * 自定義一個類,實現Runable接口 打印1-10
- *
- */
- class MyRunable implements Runnable {
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println("MyRunable:" + i);
- if (i == 2) {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- }
運行結果:
join方法你可以理解爲是直接停止了那個子線程,然後在主線程中繼續運行!
Demo:
- package com.liangdianshui;
- import java.util.Date;
- public class ThreadRunable {
- public static void main(String args[]) {
- MyThread1 t1 = new MyThread1("t1");
- MyThread1 t2 = new MyThread1("t2");
- t1.start();
- t2.start();
- }
- }
- class MyThread1 extends Thread {
- private String strName;
- public MyThread1(String str) {
- strName = str;
- }
- public void run() {
- for (int i = 0; i < 10; i++) {
- System.out.println(strName + ":" + i);
- try {
- Thread.sleep(1000); //爲了更好的顯示效果,增加延遲
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- if (i % 2 == 0) {
- yield();
- }
- }
- }
- }
四、線程的優先級別五、線程同步六、總結
這篇博文首先簡單的介紹了什麼是進程和線程,知道單個CPU不是真正意義上的多進程,CPU把自己的時間分成一個個小時間片,我這個時間片執行你一會,下一個時間片執行他一會,加上CPU的運行速度很快,因此像同時運行多個程序一樣,然後瞭解實現Thread的兩種方法,一種是實現Runable接口,一種是繼承Thread類,最後講了線程的一些方法!