一、進程和線程
1、進程
(1)概念
進程 (Process) 是計算機中的程序關于某數據集合上的一次運行活動
是系統進行資源分配的基本單位
簡單理解:程序的執行過程(正在運行的應用程序)
(2)特性
獨立性:每一個進程都有自己的空間,在沒有經過進程本身允許的情況下,一個進程不可以直接訪問其它的的進程空間
動態性:進程是動態產生,動態消亡的
并發性:任何進程都可以同其它進程一起并發執行
Tips:
并行:在同一時刻,有多個指令在多個CPU上【同時】執行
并發:在同一時刻,有多個指令在單個CPU上【交替】執行
多進程同時工作:對于一個CPU(單核),它是在多個進程間輪換執行的
2、線程
(1)概念
線程(Thread):進程可以同時執行多個任務,每個任務就是線程
(2)多線程的意義
提高執行效率;同時處理多個任務
隨著處理器上的核心數量越來越多,現在大多數計算機都比以往更加擅長并行計算
但是,一個線程,在一個時刻,只能運行在一個處理器核心上
Java程序也是一個進程,如果是一個單線程程序,則無法調動處理器的多個核心
二、Java中開啟線程的方式
Tips:Java程序默認是多線程的,一條主線程,一條垃圾回收線程
1、法一:繼承Thread類
步驟:
(1)編寫一個類繼承Thread類
(2)重寫run方法
(3)將線程任務寫在run方法中
(4)創建線程對象
(5)調用start方法開啟線程
注意:直接調用run方法并不能開啟線程
2、法二:實現Runnable接口
(擴展性更好)
步驟:
(1)編寫一個類實現Runnable接口
(2)重寫run方法
(3)將線程任務寫在run方法中
(4)創建線程任務資源對象
(5)創建線程對象,將資源傳入
(6)使用線程對象調用start方法開啟線程
public class ThreadDemo2 {public static void main(String[] args) {
// (4)創建線程任務資源對象MyRunnable mr = new MyRunnable();
// (5)創建線程對象,將資源傳入Thread t1 = new Thread(mr);
// (6)使用線程對象調用start方法開啟線程t1.start();for (int i = 0; i < 100; i++) {System.out.println("main" + i);}}
}//(1)編寫一個類實現Runnable接口
class MyRunnable implements Runnable {
// (2)重寫run方法@Overridepublic void run() {
// (3)將線程任務寫在run方法中for (int i = 0; i < 100; i++) {System.out.println("MyRunnable" + i);}}
}
3、法三:實現Callable接口
(線程任務有返回值)
步驟:
(1)編寫一個類實現Callable接口
(2)重寫call方法
(3)將線程任務寫在call方法中
(4)創建線程任務資源對象
(5)創建線程任務對象,封裝線程資源
(6)創建線程對象,傳入線程任務
(7)使用線程對象調用start方法開啟線程
public class ThreadDemo3 {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable mc = new MyCallable();FutureTask<Integer> task = new FutureTask<>(mc);Thread thread = new Thread(task);thread.start();Integer result = task.get(); // 獲取線程任務的返回值System.out.println(result);}
}class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum;}
}
三、線程的相關方法
String?getName?() | |
void?setName?(String?name) | |
static?Thread?currentThread() | |
static?void?sleep(long?time) | |
setPriority(int?newPriority) | |
final?int?getPriority() | |
final?void?setDaemon(boolean?on) |
Tips:線程的調度方式分為搶占式調度(隨機)和非搶占式調度(輪流)
Java 采用的方式是搶占式調度
提高線程的優先級可以提高該線程搶到CPU的概率
四、線程安全和同步
1、安全問題出現的條件
是多線程環境
有共享數據
有多條語句操作共享數據
2、同步技術
將多條語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程可以執行
(1)同步代碼塊
格式:
synchronized(鎖對象) {多條語句操作共享數據的代碼
}示例:
public class TicketDemo {public static void main(String[] args) {// 只new了一個TicketTask對象,三個線程共享一份數據TicketTask ticket = new TicketTask();Thread t1 = new Thread(ticket);Thread t2 = new Thread(ticket);Thread t3 = new Thread(ticket);t1.start();t2.start();t3.start();}
}class TicketTask implements Runnable {private int tickets = 2000;@Overridepublic void run() {while (true) {// 建議使用字節碼文件作為鎖對象synchronized (TicketTask.class) {if (tickets <= 0) {break;}System.out.println(Thread.currentThread().getName() + " sold " + tickets);tickets--;}}}
}
Tips:鎖對象可以是任意對象,但是需要保證多條線程的鎖對象,是同一把鎖
同步可以解決多線程的數據安全問題,但是也會降低程序的運行效率
(2)同步方法
在方法的返回值類型前面加入 synchronized?關鍵字
該方法里的代碼就變成同步的
靜態方法的鎖對象是字節碼對象,非靜態方法的鎖對象是 this
(3)Lock 鎖
使用 Lock 鎖,可以更清晰地看到哪里加了鎖,哪里釋放了鎖
Lock 是接口,無法直接創建對象
public?ReentrantLock() | |
void?lock() | |
void?unlock(); |
3、死鎖
兩個或者多個線程互相持有對方所需要的資源
導致這些線程處于等待狀態,無法前往執行
產生死鎖的情況:同步嵌套