多執行緒筆記

歪瑞古德發表於2020-09-03

1、執行緒的狀態

狂神說多執行緒講解

休眠方法:

public static void sleep(long millis)

放棄方法:放棄對時間片的使用

public static void yield()

優先順序方法、守護執行緒方法:

守護執行緒的作用:

JVM 程式在什麼情況下能夠正常退出?

答:當 JVM 中不存在任何一個正在執行的非守護執行緒時,則 JVM 程式即會退出。

如果 JVM 中沒有一個正在執行的非守護執行緒,這個時候,JVM 會退出。換句話說,守護執行緒擁有自動結束自己生命週期的特性,而非守護執行緒不具備這個特點

2、多執行緒特點以及建立方式

多執行緒的特點是搶佔式
cpu時間片安排執行緒的訪問時間

建立執行緒的方式

繼承Thread方法重寫run()方法
1.用this.getId()和this.getName()獲取執行緒的id和名稱
2.用Thread.currentThread()獲取當前執行緒  //如果當前執行緒沒有繼承Thread類的時候用
3.修改執行緒名稱:呼叫setName方法;利用構造方法 super(name);
public class ThreadTest extends Thread {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "執行緒方法被呼叫了");
    }

    public static void main(String[] args) {
        ThreadTest threadTest = new ThreadTest();
        threadTest.start();
    }
}
第二種建立執行緒的方法:
繼承Runnable介面,重寫run方法//這個類是一個run方法的實現類,在main方法中建立Thread執行緒物件時把該方法類傳入Thread中可以建立子執行緒

可以使用匿名內部類的方法直接進行執行緒建立
public class TestRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("實現了run方法");
    }
}

  public static void main(String[] args) {

        Thread thread = new Thread(new TestRunnable());
        thread.start();
    }

實現Callable介面,重寫call方法,
- 可以定義返回值
- 可以丟擲異常
public class TestCallable implements Callable<Boolean> {
     @Override
     public Boolean call() throws Exception {
         System.out.println("建立成功");
         return true;
     }
 
     public static void main(String[] args) {
         TestCallable callable = new TestCallable();
 
         //建立執行服務
         ExecutorService service = Executors.newFixedThreadPool(1);
         //提交執行
         Future<Boolean> result = service.submit(callable);
 
         boolean isTrue = result.get();
         service.shutdownNow();
 
     }
 
 }

但是,Runnable缺少的一項功能是,當執行緒終止時(即run()完成時),我們無法使執行緒返回結果。為了支援此功能,Java中提供了Callable介面。

為了實現Runnable,需要實現不返回任何內容的run()方法,而對於Callable,需要實現在完成時返回結果的call()方法。請注意,不能使用Callable建立執行緒,只能使用Runnable建立執行緒。
另一個區別是call()方法可以引發異常,而run()則不能。
為實現Callable而必須重寫call方法。

3、如何保證執行緒安全性

1.

同步程式碼塊
synchronized{

}

注意執行緒中的迴圈會一直執行,但是會根據時間片分配的時間一直執行,不會從頭開始迴圈

2.

當使用靜態方法時候鎖是(類.class)也就是當前類的型別,非靜態方法鎖是this

3. 死鎖

4、執行緒的同步非同步

Lock鎖

lock鎖和synchronized對比

Lock是顯示鎖(手動開啟和關閉,別忘記關閉鎖) synchronized是隱式鎖,出了作用域自動釋放

Lock只有程式碼塊鎖,synchronized有程式碼塊鎖和方法鎖

使用lock鎖,JVM將花費較少的時間來排程執行緒,效能更好,並且具有更好的擴充套件性(提供更多的子類)

優先使用順序

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


public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}


class TestLock2 implements Runnable {

    int ticketNums = 10;
    //定義Lock鎖
    private ReentrantLock reentrantLock = new ReentrantLock();


    @Override
    public void run() {

        while (true) {
            try {
                reentrantLock.lock(); //加鎖
                if(ticketNums > 0 ){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "購買了第" + ticketNums-- + "張票");

                } else {
                    break;
                }
            } finally {
                //解鎖
                reentrantLock.unlock();
            }

        }
    }
}

5、讀寫鎖、互斥鎖

讀寫鎖的效率比互斥鎖高很多

讀寫鎖關係:寫和寫、寫和讀互斥、讀和讀不互斥

互斥鎖全部都互斥

3、使用執行緒池

  • 背景: 經常建立和銷燬,使用量特別大的資源,比如併發情況下的執行緒,對效能影響很大
  • 思路: 提前建立好多個執行緒,放入執行緒池中,使用時直接獲取,使用完畢放回池中,可以避免頻繁的建立銷燬,實現重複利用,類似生活中的工共交通工具
  • 好處
    • 提高了響應速度(減少了建立新執行緒的時間)
    • 降低資源消耗(重複利用執行緒池中執行緒,不需要每次都建立)
    • 便於執行緒管理(…)
      • corePoolSize: 核心池的大小
      • maximumPoolSize: 最大執行緒數
      • keepAliveTime: 執行緒沒有任務時最多保持多長時間後會終止

blic class Main {
    public static void main(String[] args) {
        TV tv = new TV();
        new Watcher(tv).start();
        new Player(tv).start();
    }
}

相關文章