關鍵CSS和Webpack: 減少阻塞渲染的CSS的自動化解決方案
併發:兩個或多個事件在用一時間段內發生(交替執行);
並行:兩個或多個事件在同一時刻同時發生(如多核CPU同時處理多個執行緒)。
程序:程式執行/資源分配的最小單位,程序內資源共享,一個應用可以有一個或多個程序。
執行緒:執行緒是程序執行的單元,是CPU排程的最小單位。
程序執行緒其他:
- 程序間不會相互影響,一個執行緒掛掉會導致整個程序掛掉。
- 程序可以給記憶體上鎖:一個執行緒用完,別的執行緒才能用(互斥鎖)。
- 程序可是限制記憶體使用量:如最多3個執行緒同時使用。
執行緒/CPU排程:
- 分時排程:平均時間
- 搶佔式排程:優先順序高的先執行(JVM就是這種方式)
建立多執行緒
執行緒常用的方法:
Thread.currentThread().getName();
獲取當前執行緒名稱mt.setName("xxx");
設定執行緒名稱(mt是執行緒建立的物件)Thread.sleep(1000);
設定執行緒停止1000毫秒
建立執行緒的兩種方式:
- 繼承
java.lang.Thread
類,重寫run()方法,建立物件,呼叫start()方法。 - 實現
java.lang.Runable
介面,重寫run()方法,建立物件,賦給Thread物件。
Runable的好處:
- Runable避免了單繼承的侷限性
- 增強了程式的擴充套件性
public class Test { public static void main(String[] args) { Runnable r = new MyRunable(); Thread t = new Thread(r); t.start(); } } class MyRunable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
執行緒安全
多個執行緒訪問同一資源時,容易出現執行緒安全(如賣出同樣的車票)。此時需要使用JAVA的同步機制,有三種方式:
- 同步程式碼塊:用synchronized關鍵字修飾程式碼塊
- 同步方法:把synchronized修飾方法
- Lock鎖:比synchronized更全面、更面向物件的方法。
同步程式碼塊:synchronized關鍵字用於方法中的程式碼塊,表示對該區域資源進行互斥訪問。
同步鎖:最多允許一個執行緒擁有同步鎖,誰拿到鎖就進入程式碼塊,其他的執行緒只能在外等著。同步鎖只是一個概念,在程式設計時鎖物件可以是任意型別。
public class Test { public static void main(String[] args) { MyRunableImpl r = new MyRunableImpl(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); t1.start(); t2.start(); t3.start(); } } class MyRunableImpl implements Runnable { private int ticker = 100; Object lock = new Object(); // 同步鎖,只是一個概念,鎖物件可以是任意型別 @Override public void run() { while (true) { synchronized (lock) { if (ticker > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": " + ticker); ticker--; } } } } }
同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A執行緒執行該方法的時候,其他執行緒只能在方法外
等著。如:(它的同步鎖就是該類的物件this
,靜態方法就是類本身xxx.class
位元組碼檔案)
public static synchronized void method(){可能存線上程安全問題的程式碼}
Lock鎖:也叫同步鎖,三步完成,如下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyRunableImpl implements Runnable {
private int ticker = 100;
// 1. 在成員位置建立一個ReentrantLock物件
Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
// 2. 在問題程式碼塊前呼叫lock獲取鎖
l.lock();
if (ticker > 0) {
try {
Thread.sleep(100);
String name = Thread.currentThread().getName();
System.out.println(name + ": " + ticker--);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 3. 問題程式碼後unlock釋放鎖
l.unlock();
}
}
}
}
}
執行緒狀態
- [
NEW
]新建狀態:剛給建立,等待start()呼叫; - [
RUNNABLE
]可執行狀態:搶到CPU,要麼在執行,要麼在佇列中等待執行; - [
BLOCKED
]阻塞狀態:資源被鎖住,等待資源解鎖; - [
WAITING
]無限等待狀態:該執行緒需要別的執行緒來喚醒; - [
TIMED_WAITING
]睡眠狀態:等待自己的睡眠時間結束; - [
TERMINATED
]死亡狀態:執行結束。
睡眠狀態方法:
- sleep(5000)
- wait(5000)
無限等待狀態喚醒方法:
- notify():喚醒一個執行緒
- notifyAll():喚醒所有等待的執行緒
無限等待狀態:wait()和notify()
實現流程:
- 消費者執行緒:呼叫wait(),等待被喚醒
- 生產者程序:呼叫notify(),喚醒消費者
注意:
- 等待和喚醒只有一個在執行,必須使用同步程式碼塊包裹起來
- 使用鎖物件必須唯一
public class Test {
public static void main(String[] args) {
Object obj = new Object();
new Thread() {
@Override
public void run() {
// 1. 呼叫wait()
synchronized (obj) {
System.out.println("我要兩個包子...");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 3. 被喚醒之後
System.out.println("吃包子....");
}
}.start();
new Thread() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 2. 喚醒 notify()
synchronized (obj) {
System.out.println("包子做好了...");
obj.notify();
}
}
}.start();
}
}
執行緒通訊:等待喚醒機制
為什麼要執行緒之間通訊:執行緒完成同一任務時,有時需要操作同一份資料。
如何保證執行緒間通訊有效利用資源:避免對同一資源的爭奪,等待喚醒機制。
等待喚醒方法:
- wait():執行緒不再活動,等待被喚醒
- notify()/notifyAll():喚醒一個/所有執行緒。
注意:
- wait()和notify()必須要由同一個鎖物件呼叫;
- 鎖物件可以是任意的,都屬於Object類,所以wait()和notify()屬於Object的方法;
- wait()和notify()必須在同步程式碼塊或同步函式中使用。
執行緒池
如果執行緒很多,每個很快執行完畢,就會導致建立和銷燬執行緒比使用執行緒時間還長。有沒有方法可以使執行緒複用,執行完一個任務,不被銷燬,繼續執行其他任務。JAVA中執行緒池就是這個作用。
執行緒池:容納多個執行緒的容器,執行緒可以複用。
執行緒池好處:
- 提高響應速度:不用等待執行緒建立
- 降低資源消耗:減少建立銷燬次數,每個執行緒被重複利用。
- 提高執行緒的可管理性:調整執行緒數量,防止過多執行緒消耗記憶體。
執行執行緒池的工具:java.util.concurrent.Executor
,可以呼叫裡面的靜態方法,建立執行緒池物件。
- 建立執行緒池物件:
public static ExecutorService newFixedThreadPool(int nThreads)
- 實現
Runnable
介面,重寫run方法,設定執行緒任務 - 呼叫
ExecutorService
的submit方法,傳遞執行緒任務,開啟執行緒 - 銷燬執行緒池(一般不用,好不容易建立了,為什麼要馬上銷燬)
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class Test {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
for (int i = 0; i < 100; i++) {
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
}
}
}
class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}