1. 程式人生 > 實用技巧 >java 多執行緒-4

java 多執行緒-4

十四、sleep方法和wait方法的區別

【面試題】

  • 相同點:
    1. 一旦執行方法,都可以使得當前執行緒進入阻塞狀態。
  • 不同點:
    1. 兩個方法的宣告位置不同:Thread類宣告sleep();Object類中宣告wait()
    2. 呼叫的要求不同:sleep()可以在任何需要的場景下呼叫;wait()必須使用在同步程式碼塊或者同步方法中
    3. 關於是否收釋放同步監視器:如果有兩個方法都使用在同步程式碼塊或同步方法中,sleep()不會釋放同步監視器,而wait方法會釋放鎖

十五、JDK5.0新增執行緒建立方式

因此,java中有四種建立多執行緒的方式:

  1. 繼承Thread類,重寫run方法
  2. 實現Runnlable介面,重寫run方法
  3. 實現Callable介面,重寫cal方法
  4. 使用執行緒池【真實開發中,多數情況下使用的是執行緒池的方式

15.1 新增方式一:實現Callable介面

15.1.1 Callable介面簡介
  1. 與使用Runnable相比, Callable功能更強大些 :
    • 相比run()方法,可以有返回值
    • 方法可以丟擲異常
    • 支援泛型的返回值
    • 需要藉助FutureTask類,比如獲取返回結果
15.1.2 藉助Future介面

Future介面

  • 可以對具體Runnable、Callable任務的執行結果進行取消、查詢是 否完成、獲取結果等。
  • FutrueTask是Futrue介面的唯一的實現類
  • FutureTask 同時實現了Runnable, Future介面。它既可以作為 Runnable被執行緒執行 ,又可以作為Future得到Callable的返回值
15.1.3 列子
/**
 * 建立執行緒的方式三:實現Callable介面
 */
public class RunCallable {
    public static void main(String[] args) {
        // 第三步:建立callable介面的實現類的物件
        ThreadCallable threadCallable = new ThreadCallable();
        //  第四步:將threadCallable物件傳入到FutureTask構造器中,建立futureTask物件
        //  【FutureTask 同時實現了Runnable, Future介面。它既可以作為Runnable被執行緒執行,又可以作為Future得到Callable的返回值】
        FutureTask futureTask = new FutureTask(threadCallable);

        // 第五步:將futureTask作為引數,傳遞到Thread類構造器中,建立Thread物件,並呼叫start方法
        Thread thread = new Thread(futureTask);
        // 第六步:開啟執行緒
        thread.start();

        try {
            // 第七步:獲取callable中cal方法的返回值
            // get方法獲取返回值
            Object o = futureTask.get();
            System.out.println("總和為:"+o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

    // 第一步:實現Callable介面
class ThreadCallable implements Callable{
    private int sum;
    // 第二步:重寫run方法
    @Override
    public Object call() throws Exception {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
            sum += i;
        }
        /*返回值是Object型別的,但是為什麼return sum沒有報錯?
        * 因為這裡做了一個自動裝箱操作,即把int型的sum,轉為Integer,而Integer繼承Object類,因此沒有報錯
        * */
        return sum;
    }
}

15.2 新增方式二:使用執行緒池

15.2.1 執行緒池簡介
  • 背景:

    經常建立和銷燬、使用量特別大的資源,比如併發情況下的執行緒, 對效能影響很大。

  • 思路:

    提前建立好多個執行緒,放入執行緒池中,使用時直接獲取,使用完放回池中。可以避免頻繁建立銷燬、實現重複利用。類似生活中的公共交通工具。

  • 好處:

    1. 提高響應速度(減少了建立新執行緒的時間)
    2. 降低資源消耗(重複利用執行緒池中執行緒,不需要每次都建立)
    3. 便於執行緒管理
      • corePoolSize:核心池的大小
      • maximumPoolSize:最大執行緒數
      • keepAliveTime:執行緒沒有任務時最多保持多長時間後會終止
15.2.2 執行緒池API

執行緒池相關API

  • JDK 5.0起提供了執行緒池相關API:ExecutorService 和 Executors

  • ExecutorService:真正的執行緒池介面。常見子類ThreadPoolExecutor

    1. void execute(Runnable command) :執行任務/命令,沒有返回值,一般用來執行 Runnable
    2. Future submit(Callable task):執行任務,有返回值,一般用來執行 Callable
    3. void shutdown() :關閉連線池
  • Executors:工具類、執行緒池的工廠類,用於建立並返回不同型別的執行緒池

    1. Executors.newCachedThreadPool():建立一個可根據需要建立新執行緒的執行緒池
    2. Executors.newFixedThreadPool(n); 建立一個可重用固定執行緒數的執行緒池
    3. Executors.newSingleThreadExecutor() :建立一個只有一個執行緒的執行緒池
    4. Executors.newScheduledThreadPool(n):建立一個執行緒池,它可安排在給定延遲後運 行命令或者定期地執行。
15.2.3 列子
public class ThreadPool {
    public static void main(String[] args) {
        // 第一步:提供指定執行緒數量的執行緒池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
//        executorService.submit();// 適合使用於callable
        // 第二步:執行指定的執行緒的操作,需要提供實現Runnable介面或Callable介面實現類的物件
        TestRunnable testRunnable = new TestRunnable();
        TestRunnable2 testRunnable2 = new TestRunnable2();
        executorService.execute(testRunnable);// 適合使用於runnable
        executorService.execute(testRunnable2);// 適合使用於runnable
        // 第三步:關閉執行緒池子
        executorService.shutdown();

    }
}

class TestRunnable implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (true) {
            if (i < 101) {
                System.out.println(Thread.currentThread().getName()+"=======:"+i);
                i++;
            }else {
                break;
            }
        }
    }
}

class TestRunnable2 implements Runnable{
    private int i = 0;
    @Override
    public void run() {
        while (true) {
            if (i < 101) {
                System.out.println(Thread.currentThread().getName()+"&&&&&&&&"+i);
                i++;
            }else {
                break;
            }
        }
    }
}