并發和并行
并行:指兩個或多個時間在同一時刻發生(同時發生);
并發:指兩個或多個事件在一個時間段内發生。
在作業系統中,安裝了多個程式,并發指的是在一段時間内宏觀上有多個程式同時運作,這在單 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();
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;
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();
}
}
}
歡迎工作一到五年的Java工程師朋友們加入Java架構開發: 854393687
群内提供免費的Java架構學習資料(裡面有高可用、高并發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!