並發和並行
並行:指兩個或多個時間在同一時刻發生(同時發生);
並發:指兩個或多個事件在一個時間段內發生。
在操作系統中,安裝了多個程序,並發指的是在一段時間內宏觀上有多個程序同時運行,這在單 CPU 系統中,每一時刻只能有一道程序執行,即微觀上這些程序是分時的交替運行,只不過是給人的感覺是同時運行,那是因為分時交替運行的時間是非常短的。
而在多個 CPU 系統中,則這些可以並發執行的程序便可以分配到多個處理器上(CPU),實現多任務並行執行,即利用每個處理器來處理一個可以並發執行的程序,這樣多個程序便可以同時執行。
目前電腦市場上說的多核 CPU,便是多核處理器,核 越多,並行處理的程序越多,能大大的提高電腦運行的效率。
注意:單核處理器的計算機肯定不能並行的處理多個任務,只能是多個任務交替的在單個 CPU 上運行。
進程和線程
進程:是指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間,一個應用程序可以同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序即是一個進程從創建、運行到消亡的過程。
線程:進程內部的一個獨立執行單元;一個進程可以同時並發的運行多個線程,可以理解為一個進程便相當於一個單 CPU 操作系統,而線程便是這個系統中運行的多個任務。
注意:1、因為一個進程中的多個線程是並發運行的,那么從微觀角度看也是有先后順序的,哪個線程執行完全取決於 CPU 的調度,程序員是干涉不了的。而這也就造成的多線程的隨機性。
2、Java 程序的進程里面至少包含兩個線程,主進程也就是 main()方法線程,另外一個是垃圾回收機制線程。每當使用 java 命令執行一個類時,實際上都會啟動一個 JVM,每一個 JVM 實際上就是在操作系統中啟動了一個線程,java 本身具備了垃圾的收集機制,所以在 Java 運行時至少會啟動兩個線程。
3、由於創建一個線程的開銷比創建一個進程的開銷小的多,那么我們在開發多任務運行的時候,通常考慮創建多線程,而不是創建多進程。
4、多線程是為了同步完成多個任務,不是為了提高程序運行效率,而是通過提高資源使用效率來提高系統的效率。比如做一個添加功能,需要向很多用戶發送短信和郵件,如果需要一分鍾,不能能讓用戶等一分鍾才響應,可以使用多線程做后台發送,如果是單核,則多個任務交替的在單個 CPU 上運行,理論上比不使用多線程耗時多,因為CPU切換任務需要耗費時間,如果是多核,則可以提高資源的使用效率,多核可以充分利用上。
如何創建多線程
第一種方法:繼承 Thread 類
步驟:1、定義一個線程類 A 繼承於 java.lang.Thread 類
2、在 A 類中覆蓋 Thread 類的 run() 方法
3、在 run() 方法中編寫需要執行的操作
4、在 main 方法(線程)中,創建線程對象,並啟動線程
創建線程類:A類 a = new A()類;
調用 start() 方法啟動線程:a.start();
/** * @author: ChenHao * 創建多線程的第一種方式,繼承java.lang.Thread類 * @Description:創建一個子線程,完成1-100之間自然數的輸出。同樣,主線程執行同樣的操作 * @Date: Created in 10:50 2018/10/29 */ public class TestThread { public static void main(String [] args){ SubThread subThread1=new SubThread(); SubThread subThread2=new SubThread(); //調用線程的start(),啟動此線程;調用相應的run()方法 subThread1.start(); subThread2.start(); //一個線程只能夠執行一次start(),start()中會判斷threadStatus的狀態是否為0,不為0則拋出異常 //subThread1.start(); //不能通過Thread實現類對象的run()去啟動一個線程,此時只是主線程調用方法而已,並沒有啟動線程 //subThread1.run(); for (int i=0;i<=100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } } //1.創建一個繼承Thread的子類 class SubThread extends Thread{ //2.重寫run方法,方法內實現此子線程要完成的功能 @Override public void run(){ for (int i=0;i<=100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
注意:1、不能通過Thread實現類對象的run()去啟動一個線程,此時只是主線程調用方法而已,並沒有啟動線程,要啟動線程,必須通過Start()方法
2、一個線程只能夠執行一次start(),start()中會判斷threadStatus的狀態是否為0,不為0則拋出異常,所以一個線程調用兩次start()會報異常
第二種方法:實現 Runnable 接口
1、Runnable接口應由任何類實現,其實例將由線程執行。 該類必須定義一個無參數的方法,稱為run 。
2、該接口旨在為希望在活動時執行代碼的對象提供一個通用協議。此類整個只有一個 run() 抽象方法
步驟:1、定義一個線程類 A 實現於 java.lang.Runnable 接口(注意:A類不是線程類,沒有 start()方法,不能直接 new A 的實例啟動線程)
2、在 A 類中覆蓋 Runnable 接口的 run() 方法
3、在 run() 方法中編寫需要執行的操作
4、在 main 方法(線程)中,創建線程對象,並啟動線程
創建線程類:Thread t = new Thread( new A類() ) ;
調用 start() 方法啟動線程:t.start();
package main.java.Thread; /** * @author: ChenHao * @Description:創建多線程的方式二:實現runnable * 對比一下繼承的方式 VS 實現的方式 * 哪個方式好?實現的方式優於繼承的方式 * why? ①避免java單繼承的局限性 * ②如果多個線程要操作同一份資源,更適合使用實現的方式 * @Date: Created in 10:50 2018/10/29 */ public class TestThread2 { public static void main(String [] args){ //此程序存在線程的安全問題,打印車票時,會出現重票、錯票,后面線程同步會講到 Window window=new Window(); Thread thread1=new Thread(window,"窗口一"); Thread thread2=new Thread(window,"窗口二"); Thread thread3=new Thread(window,"窗口三"); thread1.start(); thread2.start(); thread3.start(); } } class Window implements Runnable{ int ticket=100; @Override public void run(){ while (true){ if(ticket > 0){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"售票,票號為:"+ticket--); }else { break; } } } }
哪個方式好?實現的方式優於繼承的方式
why? ①避免java單繼承的局限性
②如果多個線程要操作同一份資源,更適合使用實現的方式
注意:此程序存在線程的安全問題,打印車票時,會出現重票、錯票,下一篇線程同步會講到
第三種方法:使用匿名內部類創建線程
public static void main(String[] args) { for(int i = 0 ; i < 10 ; i++){ System.out.println("玩游戲"+i); if(i==5){ new Thread(new Runnable() { @Override public void run() { for(int i = 0 ; i < 10 ;i++){ System.out.println("播放音樂"+i); } } }).start(); } } }