java 多線程

java 的多線程有好幾種,能夠繼承 Thread,也能夠實現 Runnable 接口,還能夠實現 Callable 接口java

Thread

class MyThread extends Thread {
    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100L);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.printf("%s is running %d\n", name, i);
        }
    }
}

{
    Thread t1 = new MyThread("t1");
    Thread t2 = new MyThread("t2");

    t1.start();
    t2.start();
    try {
        t1.join();
        t2.join();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

繼承 Thread,本身實現 run 方法,就能夠定一個線程類,調用 start 就能夠在一個新的線程裏面調用 run 方法,若是須要等待線程結束,能夠調用 join 方法git

Runnable

class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(100L);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.printf("%s is running %d\n", name, i);
        }
    }
}

{
    Thread r1 = new Thread(new MyRunnable("r1"));
    Thread r2 = new Thread(new MyRunnable("r2"));

    r1.start();
    r2.start();
    try {
        r1.join();
        r2.join();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

和 Thread 差很少,只不過不直接繼承 Thread,而是實現 Runnable 接口(Runable 只有一個 run 方法),使用上面用這個 Runnable 去構造一個 Thread,這種方式相對直接繼承 Thread 的方式要更加靈活,由於 java 是單繼承,若是繼承了 Thread 就不能再繼承別的類github

事實上,建議永遠不要直接繼承 Thread 類,由於從語義上來說,Thread 也應該也只是方法運行的方式,你的類應該是能夠在這種方式下運行,而不是一種 Thread 對象,從這個角度講,Runnable 提供了更好的語義,用一個 Thread 對象去運行一個 Runablegolang

Callable

class MyCallable implements Callable<Integer> {
    private Random random;

    public MyCallable() {
        this.random = new Random();
    }

    @Override
    public Integer call() throws Exception {
        Thread.sleep(100L);
        return this.random.nextInt();
    }
}

{
    FutureTask<Integer> future1 = new FutureTask<>(new MyCallable());
    FutureTask<Integer> future2 = new FutureTask<>(new MyCallable());
    new Thread(future1).start();
    new Thread(future2).start();

    try {
        System.out.println(future1.get(50, TimeUnit.MILLISECONDS));
    } catch (TimeoutException e) {
        System.out.println("future1 timeout");
    } catch (Exception e) {
        e.printStackTrace();
    }

    try {
        System.out.println(future2.get());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

Callable 接口也只有一個方法 call,和 Runnable 不一樣的是 Callable 容許有返回值,而這個返回值能夠經過 FutureTask.get 獲取,還能夠設置任務運行的超時時間,超時後會拋出一個異常緩存

ThreadPool

class MyCallable implements Callable<Integer> {
    private Random random;

    public MyCallable() {
        this.random = new Random();
    }

    @Override
    public Integer call() throws Exception {
        Thread.sleep(100L);
        return this.random.nextInt();
    }
}

{
    ExecutorService es = Executors.newFixedThreadPool(5);
    Future<Integer> future1 = es.submit(new MyCallable());
    Future<Integer> future2 = es.submit(new MyCallable());

    try {
        System.out.println(future1.get(50, TimeUnit.MILLISECONDS));
    } catch (TimeoutException e) {
        System.out.println("future1 timeout");
    } catch (Exception e) {
        e.printStackTrace();
    }

    try {
        System.out.println(future2.get());
    } catch (Exception e) {
        e.printStackTrace();
    }

    es.shutdown();
}

java 裏面線程的建立和銷燬成本比較大,因此通常會須要放到線程池裏面跑,java 的基礎設施就是好,這些在標準庫裏面都有實現,使用上面也很簡單,直接 new 出一個線程池就行了,而後就能夠往裏面 submit Callable 對象,線程池也有不少種,上面用到的 newFixedThreadPool 是固定線程數的線程池,下面用到的 newCachedThreadPool 在線程不夠用的時候會建立新線程,同時也會不斷複用以前建立的線程多線程

{
    ExecutorService es = Executors.newCachedThreadPool();
    CompletionService<Integer> cs = new ExecutorCompletionService<>(es);
    cs.submit(new MyCallable());
    cs.submit(new MyCallable());
    cs.submit(new MyCallable());
    cs.submit(new MyCallable());

    try {
        System.out.println(cs.take().get());
        System.out.println(cs.take().get());
        System.out.println(cs.take().get());
        System.out.println(cs.take().get());
    } catch (Exception e) {
        e.printStackTrace();
    }

    es.shutdown();
}

典型的生成者消費者模型裏面,咱們須要把生產的結果放到一個隊列裏面,而消費者從這個隊列裏面不斷地去消費,ExecutorCompletionService 就至關於這個隊列,MyCallable 的結果會寫入到緩存裏面,使用 cs.take().get() 從裏面取出結果dom

總結

線程的建立,銷燬,切換在 java 裏面都是耗性能的操做,若是有需求要大量地建立線程,儘可能使用線程池去複用線程ide

參考連接

轉載請註明出處
本文連接: http://hatlonely.com/2018/03/...
相關文章
相關標籤/搜索