1. 程式人生 > 實用技巧 >關鍵CSS和Webpack: 減少阻塞渲染的CSS的自動化解決方案

關鍵CSS和Webpack: 減少阻塞渲染的CSS的自動化解決方案

併發:兩個或多個事件在用一時間段內發生(交替執行);

並行:兩個或多個事件在同一時刻同時發生(如多核CPU同時處理多個執行緒)。

程序:程式執行/資源分配的最小單位,程序內資源共享,一個應用可以有一個或多個程序。

執行緒:執行緒是程序執行的單元,是CPU排程的最小單位。

程序執行緒其他:

  1. 程序間不會相互影響,一個執行緒掛掉會導致整個程序掛掉。
  2. 程序可以給記憶體上鎖:一個執行緒用完,別的執行緒才能用(互斥鎖)。
  3. 程序可是限制記憶體使用量:如最多3個執行緒同時使用。

執行緒/CPU排程:

  1. 分時排程:平均時間
  2. 搶佔式排程:優先順序高的先執行(JVM就是這種方式)

建立多執行緒

執行緒常用的方法:

  1. Thread.currentThread().getName(); 獲取當前執行緒名稱
  2. mt.setName("xxx");設定執行緒名稱(mt是執行緒建立的物件)
  3. Thread.sleep(1000);設定執行緒停止1000毫秒

建立執行緒的兩種方式:

  1. 繼承java.lang.Thread類,重寫run()方法,建立物件,呼叫start()方法。
  2. 實現java.lang.Runable介面,重寫run()方法,建立物件,賦給Thread物件。

Runable的好處:

  1. Runable避免了單繼承的侷限性
  2. 增強了程式的擴充套件性
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的同步機制,有三種方式:

  1. 同步程式碼塊:用synchronized關鍵字修飾程式碼塊
  2. 同步方法:把synchronized修飾方法
  3. 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();
                }
            }
        }
    }
}

執行緒狀態

  1. [NEW]新建狀態:剛給建立,等待start()呼叫;
  2. [RUNNABLE]可執行狀態:搶到CPU,要麼在執行,要麼在佇列中等待執行;
  3. [BLOCKED]阻塞狀態:資源被鎖住,等待資源解鎖;
  4. [WAITING]無限等待狀態:該執行緒需要別的執行緒來喚醒;
  5. [TIMED_WAITING]睡眠狀態:等待自己的睡眠時間結束;
  6. [TERMINATED]死亡狀態:執行結束。

睡眠狀態方法:

  1. sleep(5000)
  2. wait(5000)

無限等待狀態喚醒方法:

  1. notify():喚醒一個執行緒
  2. notifyAll():喚醒所有等待的執行緒

無限等待狀態:wait()和notify()

實現流程:

  1. 消費者執行緒:呼叫wait(),等待被喚醒
  2. 生產者程序:呼叫notify(),喚醒消費者

注意:

  1. 等待和喚醒只有一個在執行,必須使用同步程式碼塊包裹起來
  2. 使用鎖物件必須唯一
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();
    }
}

執行緒通訊:等待喚醒機制

為什麼要執行緒之間通訊:執行緒完成同一任務時,有時需要操作同一份資料。

如何保證執行緒間通訊有效利用資源:避免對同一資源的爭奪,等待喚醒機制。

等待喚醒方法:

  1. wait():執行緒不再活動,等待被喚醒
  2. notify()/notifyAll():喚醒一個/所有執行緒。

注意:

  1. wait()和notify()必須要由同一個鎖物件呼叫;
  2. 鎖物件可以是任意的,都屬於Object類,所以wait()和notify()屬於Object的方法;
  3. wait()和notify()必須在同步程式碼塊或同步函式中使用。

執行緒池

如果執行緒很多,每個很快執行完畢,就會導致建立和銷燬執行緒比使用執行緒時間還長。有沒有方法可以使執行緒複用,執行完一個任務,不被銷燬,繼續執行其他任務。JAVA中執行緒池就是這個作用。

執行緒池:容納多個執行緒的容器,執行緒可以複用。

執行緒池好處:

  1. 提高響應速度:不用等待執行緒建立
  2. 降低資源消耗:減少建立銷燬次數,每個執行緒被重複利用。
  3. 提高執行緒的可管理性:調整執行緒數量,防止過多執行緒消耗記憶體。

執行執行緒池的工具:java.util.concurrent.Executor,可以呼叫裡面的靜態方法,建立執行緒池物件。

  1. 建立執行緒池物件:public static ExecutorService newFixedThreadPool(int nThreads)
  2. 實現Runnable介面,重寫run方法,設定執行緒任務
  3. 呼叫ExecutorService的submit方法,傳遞執行緒任務,開啟執行緒
  4. 銷燬執行緒池(一般不用,好不容易建立了,為什麼要馬上銷燬)
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());
   }
}