標籤:提高 時間段 alt 路徑 nal call java虛擬機器 重寫 好處
1 進程
- 進程:
- 正在運行中的程式,是系統進行資源分派和調用的獨立單位。
- 每一個進程專屬它自己的記憶體空間和系統資源。
- 多進程的意義:
- 單進程電腦只能做一件事情,而我們現在的電腦都可以一邊玩遊戲,一邊聽音樂,所以我們常見的作業系統都是多進程作業系統。比如Linux和Mac等,能在同一段時間內執行多個任務。
- 對於單核電腦而言,遊戲進程和聽音樂進程是同時啟動並執行,當然不是,因為CPU在某個時間點上只能做一件事情,電腦是在遊戲進程和聽音樂進程間做頻繁切換,且切換速度很快,所以,我們感覺遊戲和音樂是同時進行的,其實並不是同時進行的。
- 多進程的作用不是提高執行速度,而是為了提高CPU的使用效率。
2 線程
- 線程是進程中的單個順序控制流程,是一條執行路徑。或者換句話說,一個進程內部可以執行多個任務,而每一個任務我們都可以看成是一個線程。
- 一個進程如果只有一條執行路徑,就稱為單線程程式。
- 一個進程如果有多條執行路徑,就稱為多線程程式。
3 多線程存在的意義
- 多線程的作用不是為了提高CPU的使用效率,而是為了提高應用程式的使用效率。
- 多線程給我們的錯覺就是讓我們認為多個線程是並發執行的,其實不然,因為多個線程是共用一個進程的資源(堆記憶體和方法區),但是棧記憶體是獨立的,一個線程一個棧,所以他們仍然是在搶CPU的資源執行。一個時間點上只有一個線程執行,而且誰搶到,不一定,所以,造成了線程啟動並執行隨機性。
- 並發:是一個時間段同時運行多個程式。
- 並行:是一個時間點上同時運行多個程式。
4 java程式的運行原理
- java命令會啟動java虛擬機器,啟動JVM,等於啟動了一個應用程式,也就是啟動了一個進程。該進程會自動啟動一個“主線程”,然後主線程去調用某個類的main方法,所以main方法運行在主線程中。但是JVM啟動還會啟動記憶體回收線程,所以java程式是多線程的。
package java012;/** * 2017/9/29 * 說明: */class Demo { public void finalize(){ System.out.println("我要死了"); }}public class ThreadDemo { public static void main(String[] args) { new Demo(); new Demo(); System.gc(); new Demo(); System.out.println("你好世界"); }}
5 多線程的好處和弊端
- 好處:“適當”提高程式的執行效率、“適當”提高CPU和記憶體的利用率、線程執行完成後會自動銷毀節省記憶體。
- 弊端:每開啟一條線程就會佔用一定記憶體空間,降低程式效能;線程越多,CPU調度(多個線程之間切換)開銷越大,時間開銷、空間開銷。
6 線程的建立方式一-
- 建立線程的步驟:
- ①定義一個類繼承Thread類
- ②覆蓋Thread類中的run方法
- ③建立Thread類的子類對象
- ④調用start()方法,啟動線程並調用自訂線程的run()方法
- 解釋為什麼建立線程需要繼承Thread,並重寫run方法。
- 建立線程的目的是為了開啟一條執行路徑,去運行指定的代碼和其他代碼並發運行。而運行指定的代碼就是這個執行路徑的任務。JVM建立的主線程的任務都是定義在main方法中, 那麼自訂的線程的任務在哪裡你?Thread類用於描述線程,線程需要任務的,所以Thread提供了對任務的描述,這個任務就是Thread類的run方法來體現的,也就是說,run方法就是封裝自訂線程運行任務的函數。那麼此時你可能會想,我們直接new Thread().run()方法不就可以了,那麼非常抱歉,此時,啟動並執行不是我需要並發執行的代碼,而是Thread類中的run()方法中的代碼,可惜的是,不是我要的,所以,這就是為什麼在java中,我們建立線程需要繼承Thread類,並重寫run()方法的原因。
package java012;/** * 2017/9/29 * 說明: */class Demo extends Thread { private String name; public Demo(String name){ this.name = name; } public void run(){ this.show(); } public void show(){ for(int x =0;x<100;x++){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name+":"+x); } }}public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("d1"); Demo d2 = new Demo("d2"); d1.start(); d2.start(); }}
6.1 線程的一些方法
public final String getName()
package java012;/** * 2017/9/29 * 說明: */class Demo extends Thread { private String name; public Demo(String name){ this.name = name; } public void run(){ this.show(); } public void show(){ for(int x =0;x<10;x++){ System.out.println(name+":"+x+":線程的名稱:"+getName()); } }}public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("d1"); Demo d2 = new Demo("d2"); d1.run(); d2.run(); }}
結果好像不對,因為我們並沒有調用start()方法,那麼此時啟動並執行列印的線程的名稱應該是main之類的。對,應該列印的是main之類的,但是,我們要知道,我們調用的是Thread的無參構造方法。代碼如下所示:
package java012;/** * 2017/9/29 * 說明: */class Demo extends Thread { private String name; public Demo(String name){ super();//調用父類的無參構造方法 this.name = name; } public void run(){ this.show(); } public void show(){ for(int x =0;x<10;x++){ System.out.println(name+":"+x+":線程的名稱:"+getName()); } }}public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("d1"); Demo d2 = new Demo("d2"); d1.run(); d2.run(); }}
那麼,此時我們觀察Thread的源碼就可以知道答案:
/** * Allocates a new {@code Thread} object. This constructor has the same * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} * {@code (null, null, gname)}, where {@code gname} is a newly generated * name. Automatically generated names are of the form * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer. */ public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); }
這下知道原因了吧。
public static Thread currentThread()
package java012;/** * 2017/9/29 * 說明: */class Demo extends Thread { private String name; public Demo(String name){ super();//調用父類的無參構造方法 this.name = name; } public void run(){ this.show(); } public void show(){ for(int x =0;x<10;x++){ System.out.println(name+":"+x+":線程的名稱:"+Thread.currentThread().getName()); } }}public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("d1"); Demo d2 = new Demo("d2"); d1.run(); d2.run(); }}
上面的代碼證明了2點:①如果我們不調用自訂線程的start()方法,就不是開啟新的線程②想擷取運行時的線程,必須通過Thread.currentThread()。
package java012;/** * 2017/9/29 * 說明: */class Demo extends Thread { private String name; public Demo(String name){ super();//調用父類的無參構造方法 this.name = name; } public void run(){ this.show(); } public void show(){ for(int x =0;x<10;x++){ System.out.println(name+":"+x+":線程的名稱:"+Thread.currentThread().getName()); } }}public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("d1"); Demo d2 = new Demo("d2"); d1.start(); d2.start(); System.out.println("主線程的名字:"+Thread.currentThread().getName()); }}
public Thread(String name)
package java012;/** * 2017/9/29 * 說明: */class Demo extends Thread { private String name; public Demo(String name){ super(name);//調用父類的有參構造方法,給線程取定義的名稱 this.name = name; } public void run(){ this.show(); } public void show(){ for(int x =0;x<10;x++){ System.out.println(name+":"+x+":線程的名稱:"+Thread.currentThread().getName()); } }}public class ThreadDemo { public static void main(String[] args) { Demo d1 = new Demo("d1"); Demo d2 = new Demo("d2"); d1.start(); d2.start(); System.out.println("主線程的名字:"+Thread.currentThread().getName()); }}
多線程(一)