并發(1) -- 線程與線程池

更快的執行

在多核處理器上,將一個程序的分為多個片段,在每個單獨的處理上運行每個片段。利用上更多的處理器顯然會使程序運行的更加快。
但并發通常是提高運行在單核處理器上的程序性能。理論上,單處理器上運行并發任務增加了上下文切換的時間,這會看起來比順序執行開銷更大。
但實際上,阻塞的出現讓情況變得不同。如果程序中某個任務出現線程阻塞(通常是I/O),這時候使用并發編寫程序,可以保證其他任務可以繼續執行。

基本線程機制

代表所執行的對象的 Runnable

實現 Runnable 接口 唯一 public abstract void run() 方法,代表所須執行的命令。

public class Demo implements Runnable {

    private int countDown = 10;

    @Override
    public void run() {
        while (countDown > 0){
            System.out.println(countDown--);
            Thread.yield(); //讓步
        }
    }
}

帶返回值的 Callable

實現 Callable 唯一方法 V call() 方法。

public class CallDemo implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "CallDemo" + super.toString();
    }
}

Future<?> submit(Runnable task) 方法返回了 Future<?> 接口的實例。
我們可以使用 isDone 來檢驗 Future 對象是否執行完異步的任務,也可以不檢查直接使用 get() 獲取異步任務的返回對象,這個過程也會阻塞到結果異步任務執行完。

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<String> future = executorService.submit(new CallDemo());
        executorService.shutdown();
        System.out.println(future.get());
    }
}

FutureTask

FutureTask<V> 實現了 RunnableFuture<V> 接口。而 RunnableFuture<V> 多重繼承了 RunnableFuture<V> 方法。

public class FutureTask<V> implements RunnableFuture<V>{...}

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

所以 FutureTask 可以直接在構造器中聲明所執行的異步任務,即 Runnable 對象。在提交到執行器(ExecutorService)之后,可以通過自己的 get() 方法獲取異步任務的返回值。

public class Main {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        FutureTask<String> futureTask = new FutureTask<>(new CallDemo());
        executorService.submit(futureTask);
        System.out.println(futureTask.get());//忽略了 try catch
        executorService.shutdown();
    }
    
}

Thread

Thread.yield() 的調用是對線程調度器的一個建議,表示自己已經執行完了重要的代碼,可以切換到其他任務。

   public static void main(String[] args) {
        Thread thread = new Thread(new Demo());
        thread.start();
    }

Thread.start() 方法為該線程執行必須的初始化操作。然后在新的線程中調用 Runnable.run() 方法。

Executor

Executor 允許你管理異步任務的執行,而無需顯示的管理線程的生命周期。類命令模式的設計使其只能提供了一個 excute() 方法。

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            executorService.execute(new Demo());
        }
        executorService.shutdown();
        System.out.println("main end");
    }
}

3 3 2 3 3 3 2 1 2 2 2 1 main end 1 1 1
Process finished with exit code 0

shutDown() 方法保證在這之后有新的任務提交給 Executor ,而后在 Executor 執行完成任務后盡快退出。

  • CachedThreadPool
WX20171030-160639@2x.png

CachedThreadPool 會為創建所屬數量的線程,只有在回收線程時停止創建新的線程,而 maximumPoolSize 值為 Integer.MAX_VALUE,有可能因線程數過多而倒是 OOM。

  • FixedThreadPool

FixedThreadPool 可以限制線程數量。一次性的預先執行代價高昂的線程分配,而不用為每個任務都付出創建線程的開銷。

  • SingleThreadExecutor
WX20171030-160410@2x.png

SingleThreadExecutor 是線程數為 1 的 FixedThreadPool 的串行隊列。maximumPoolSize 值為 1 , 會因為任務堆積而導致 OOM。

Sleep

sleep() 使任務中止運行一段時間。

public class Demo implements Runnable {

    private int countDown = 3;

    @Override
    public void run() {
        try {
            while (countDown > 0) {
                System.out.println(countDown--);
                TimeUnit.MILLISECONDS.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

權重(Priority)

Thread.currentThread().setPriority(priority); 可以為任務設置優先級。確保線程調度機制能優先運行最高優先級的任務。

讓步(yield)

在完成一次迭代后,可以給線程調度機制一個建議,調用 yield() 方法讓步,表示這個時刻可以運行 具有相同優先級的 的任務。

后臺線程(Daemon)

后臺(daemon)線程指程序在運行時在后臺提供一種通用服務的線程,并且這種線程不屬于程序中不可或缺的部分。當所有非后臺線程結束后,程序也中止了。

Thread daemon = new Thread(new Demo());
daemon.setDaemon(true);
daemon.start();

Process finished with exit code 0 (不執行 runnable 中的任務)

線程工廠(ThreadFactory)

我們可以繼承 ThreadFactory 并實現其 newThread() 方法,來配置 ExecutorService 所創建線程的屬性。

public class DaemonThreadFactory implements ThreadFactory{

    @Override
    public Thread newThread(Runnable r) {
        Thread thread = new Thread();
        thread.setDaemon(true);
        return thread;
    }
}

使用 ThreadFactory 來構建 newCachedThreadPool

public class Main {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool(new DaemonThreadFactory());
        service.execute(new Demo());
        service.shutdown();
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容