1. 程式人生 > 實用技巧 >Springboot之檔案監控

Springboot之檔案監控

程式:一段靜態的程式碼

程序:正在執行的一個程式,系統在執行時為每一個程序分配不同的記憶體區域。

執行緒:程序的進一步細化,與程序共享一個記憶體區域,但是有獨立的執行棧和程式計數器。

單核CPU:假的多執行緒,一個執行緒執行一次,時間間隔較短,類似多車道,一個收費站。

多核CPU:更好的發揮多執行緒的效率。

一個java程式至少有三個執行緒:main()、gc()垃圾回收、異常處理執行緒。

並行:多個CPU執行多個任務

併發:一個CPU執行多個任務,如:秒殺,多人做同一件事

多執行緒的優點:

  1. 提高程式的相應,對於圖形化介面,提高使用者的體驗
  2. 提高cpu的利用率。
  3. 將程式拆分成多個任務,方便程式碼的獨立執行。

1、執行緒的建立和使用

執行緒的建立

方式一:繼承於Thread

  1. 建立一個繼承Thread類的子類
  2. 重寫run()方法
  3. 建立Thread類的子類
  4. 通過子類物件呼叫start()方法

start()方法:①啟動該執行緒 ②呼叫run()方法

注意

  1. 直接呼叫run()方法並不會開啟新的執行緒,是主執行緒正常執行run()方法。
  2. 不可以兩次執行(同一物件呼叫2次start方法)相同的執行緒,必須重新new 執行緒物件。

方式二:實現Runnable介面

  1. 建立一個實現Runnable介面的類。
  2. 實現類實現run()方法。
  3. 建立實現類物件
  4. new Thread(實現類物件)
  5. 通過Thread類的物件呼叫 start()

Thread中的常用方法:

  1. start(),啟用執行緒,呼叫run()
  2. run(),將需要多執行緒需要執行的方法寫裡面
  3. currentThread(),靜態方法,返回當前執行程式碼的執行緒
  4. getName(),獲取執行緒名
  5. setName(),設定執行緒名 / new Thread("執行緒名");
  6. yield(),釋放當前cpu執行權
  7. join():線上程A中呼叫執行緒B的join方法,此時A就進入阻塞狀態,直到B執行完,A才繼續執行。
  8. sleep(long millitime):讓當前執行緒睡眠一定的毫秒時間
  9. isAlive():判斷當前執行緒是否存活

執行緒的排程

  • 執行緒的優先順序:

MAX_PRIORITY :10

NORM_PRIORITY :5 預設優先順序

MIN_PRIORITY :1

注意:高優先順序的執行緒要搶佔低優先順序的執行緒cpu執行權,但是隻是從概率上講,並不是一定。

  • 獲取或設定當前執行緒的優先順序

getPriority(): 獲取

setPriority();設定

比較兩種建立執行緒的方式:

開發中,優先選擇實現Runnable介面的方式。

原因:

  1. 實現的方式沒有類單繼承的侷限性
  2. 實現的方式更適合處理多個執行緒共有資料的情況

2、執行緒的生命週期

  1. 新建:呼叫start()
  2. 就緒:等待取得cpu資源
  3. 執行:
  4. 阻塞:①sleep() ②join() ③等待同步鎖 ④wait()
  5. 死亡:①執行完 ②stop() ③error/exception且沒有處理。

3、執行緒的同步

在java中,我們通過同步機制,來解決執行緒的安全問題。

同步的方式:

好處:解決了執行緒的安全問題

侷限性:操作同步程式碼時,只有一個執行緒能操作,效率比較低。

方式一:同步程式碼塊

    synchronized(同步監視器){
    //需要被同步的程式碼
    }

說明:

  1. 需要操作共享資料的,就視為同步的程式碼。
  2. 同步監視器(鎖),任何一個類的物件都可以充當鎖。要求:多個執行緒共用一把鎖
  3. 在實現Runnable介面的方式中,可以使用this充當鎖
  4. 在繼承Thread的方式中,慎用this充當鎖。可考慮當前物件

方式二:同步方法

操作共享資料的程式碼完整宣告在一個方法中,可使用同步方法的方式。

實現Runnable介面的方式

public synchronize void  a(){
}

繼承Thread的方式

public static synchronize void  a(){
}

注意:

  1. 在該方式中,沒有顯式的鎖。
  2. 非靜態的同步方法,預設鎖是this。
  3. 靜態的同步方法,瑣是當前類本身。

改寫單例模式中的懶漢式為執行緒安全:

class Bank{
    private Bank(){}
    private static Bank instance = null;

    private static Bank getInstance(){
        
        if (instance == null){
            synchronized (Bank.class){
                if (instance == null){
                    instance = new Bank();
                }
            } 
        }
        return instance;
    }
}

4、執行緒的死鎖

理解:不同執行緒佔用對方需要的資源不釋放,處於僵持的狀態,都在互相等待。

說明:出現死鎖後程序並不會出現異常,只是所有執行緒都處於阻塞的狀態。

解決方法:

  1. 專門的演算法、原則
  2. 儘量減少同步資源的定義
  3. 避免巢狀同步

5、執行緒的同步--鎖

Lock鎖-----jdk5.0新增

  1. 例項化 ReentrantLock()物件

    ReentrantLock lock = new ReentrantLock();
    
  2. 呼叫鎖定方法lock.lock();

  3. 解鎖lock.unlock();

synchronize 與 Lock 異同? (面試題)

  • 相同:都可以解決執行緒安全問題

  • 不同:synchronize 機制在執行完相應的同步程式碼後,自動釋放鎖。

    ​ Lock需要手動上鎖(lock())和解鎖(unLock());

使用順序(建議/非必須)

Lock-->同步程式碼塊-->同步方法

如何解決執行緒安全問題?有幾種方式?(面試題)

三種:同步塊,同步方法,鎖Lock

6、執行緒的通訊

執行緒通訊的三個方法:

  1. wait() : 一旦執行此方法,當前執行緒就進入阻塞狀態,並釋放鎖
  2. notify() : 執行此方法,喚醒被wait()的執行緒,如果有多個,就喚醒優先順序高的
  3. notifyAll() : 執行此方法,喚醒全部wait()的執行緒

注意:

  1. 三個方法只能使用在同步程式碼塊或同步方法中。
  2. 三個方法的呼叫者,必須是同步程式碼塊或同步方法中的監視器(鎖),否者會出現異常。
  3. 三個方法定義在Object中,不是Tread中。

例項:兩個執行緒交替列印1-100

public class Test {

    public static void main(String[] args) {
        Number number = new Number();

        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("執行緒1");
        t2.setName("執行緒2");

        t1.start();
        t2.start();
    }
}


class Number implements Runnable{
    int number = 1 ;

    @Override
    public void run() {
        while (true){
            synchronized (this) {
                notify();
                if (number<=100){
                    System.out.println(Thread.currentThread().getName()+":"+number);
                    number++;
                }
                else{
                    break;
                }
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

sleep()和 wait()方法的異同? (面試題)

  • 相同:都可以使執行緒阻塞
  • 不同:
  1. 兩者宣告的位置不同,sleep()宣告在Thread類,而wait()宣告在Object類。
  2. sleep可以在任何位置使用,而wait()只能在同步程式碼塊和同步方法中使用。
  3. 當兩者都使用在同步程式碼塊和同步方法中時,sleep()不會釋放鎖,wait()會釋放鎖

例題生產者消費者

產品小於0個不能購買,大於0個可購買。

產品大於20不能再生產,不足20繼續生產。

銷售類

class Clerk{

    private int num = 0;

    public synchronized void produceProduct() {
        if(num<20){
            num++;
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":開始生產第"+num+"個產品");
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void customerProduct() {
        if(num>0){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":開始消費第"+num+"個產品");
            num--;
            notify();
        }else{
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

生產者

class Producter extends Thread{

    private Clerk clerk;

    public Producter(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while (true){
            clerk.produceProduct();
        }
    }
}

消費者

class Customer extends Thread{

    private Clerk clerk;

    public Customer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        while (true){
            clerk.customerProduct();
        }
    }
}

測試

public class ProductTest {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();

        Producter p1 = new Producter(clerk);
        Customer c1 = new Customer(clerk);
        Customer c2 = new Customer(clerk);

        p1.setName("生產者1");
        c1.setName("消費者1");
        c2.setName("消費者2");

        p1.start();
        c1.start();
        c2.start();
    }
}

7、新增執行緒的建立方式

方式一:實現Callable介面

JDK5.0 新增

與實現runnable介面相比更加強大:

  1. call方法中可以有返回值。
  2. call()可以丟擲異常,被外面的操作捕獲。
  3. Callable支援泛型。

步驟:

  1. 建立Callable的實現類
  2. 將要執行的操作寫在Callable實現類的call方法中。
  3. 建立callable的實現類物件
  4. 建立FutureTask 物件 ,callable的實現類物件作為引數。
  5. new Thread(FutureTask 物件).start()開啟執行緒;
  6. 可使用FutureTask 物件的get()方法,獲取call()方法的返回值。

方式二:使用執行緒池

思路:提前建立好多個執行緒,放入執行緒池,隨用隨取。

好處:①提高響應速度②降低資源消耗

③便於執行緒管理:

  1. 執行緒池大小
  2. 執行緒數
  3. 沒有任務的持續時間

步驟:

 //1.提供指定數量的執行緒池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //2.執行指定執行緒操作,需要提供實現runnable/callable類的物件
        executorService.execute(new Number());
		//executorService.submit(callable callable);
        //3.關閉執行緒池
        executorService.shutdown();

建立多執行緒有幾種方式?(面試題)

4種:繼承Tread

實現Runnable

實現Callable

執行緒池