Thinking in Java -- 並發(一)

來源:互聯網
上載者:User

Thinking in Java -- 並發(一)
並發的多面性

基本的並發定義任務

線程可以驅動任務,我們通過實現 Runnable 介面來提供,需要實現 Runnable 介面的 run() 方法。

package concurrency;/** * Created by wwh on 16-3-24. */public class LiftOff implements Runnable {    protected int countDown = 10;    private static int taskCount = 0;    private final int id = taskCount++;    public LiftOff() {}    public LiftOff(int countDown) {        this.countDown = countDown;    }    public String status() {        return "#" + id + "(" +                (countDown > 0 ? countDown : "LiftOff!") + "), ";    }    public void run() {        while (countDown-- > 0) {            System.out.println(status());            /* Thread.yield() 是對線程調度器的一種建議,可以將CPU從一個線程轉移給另一個線程 */            Thread.yield();        }    }}
package concurrency;/** * Created by wwh on 16-3-24. */public class MainThread {    public static void main(String[] args) {        LiftOff lauch = new LiftOff();        lauch.run();    }}

我們也可以通過繼承 Thread 類覆蓋 run() 方法來實現線程類,但繼承Thread類有一個缺點就是單繼承,而實現Runnable介面則彌補了它的缺點,可以實現多繼承。而且實現 Runnable 介面適合多線程共用資源,繼承 Thread 類適合各個線程完成自己的任務,因為繼承 Thread 類相當於每個線程有一份各自的資源,而實現 Runnable 還可以讓多個線程共用一份代碼。

Thread 類

將 Runnable 對象轉變為工作任務的傳統方式是將它提交給一個 Thread 構造器。

public class BasicThreads {    public static void main(String[] args) {        for (int i = 0; i < 5; ++i) {            new Thread(new LiftOff()).start();        }        System.out.println("Waiting for LiftOff");    }}

Thread 構造器只需要一個 Runnable 對象。調用 Thread 對象的 start() 方法為該線程執行必須的初始化操作,然後內部調用 Runnable 的 run() 方法。

使用 Executor

Java SE5 並發包中引入執行器可以為我們管理線程 Thread 對象。

package concurrency;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * Created by wwh on 16-3-24. */public class CachedThreadPool {    public static void main(String []args) {        /* ExecutorServive 是具有聲明周期的 Executor */        ExecutorService exec = Executors.newCachedThreadPool();        for (int i = 0; i < 5; ++i) {            exec.execute(new LiftOff());        }        /* shutdown 被用來防止新任務被提交給 Executor */        exec.shutdown();    }}

ThreadPool 種類很多。如:

包括
CacheThreadPool:為每個任務都建立一個線程池
FixedThreadPool:一次性預先分配好固定大小的線程
SingleThreadExecutor:線程數唯一,提交多個任務會排隊等候
ScheduledThreadPool:建立一個定長線程池,支援定時及週期性任務執行。<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjwvcD4NCjxociAvPg0KPGgyIGlkPQ=="從任務中產生傳回值">從任務中產生傳回值

如果我們希望任務完成時能夠返回一個值,那麼可以實現 Callable 介面而不是 Runnable 介面,實現 Callable 介面要求覆蓋 Call() 方法。

package concurrency;import java.util.ArrayList;import java.util.concurrent.*;/** * Created by wwh on 16-3-24. */class TaskWithResult implements Callable { private int id; public TaskWithResult(int id) { this.id = id; } public String call() throws Exception { return "reslut of TaskWithResult " + id; }}public class CallableDemo { public static void main(String []args) { ExecutorService exec = Executors.newCachedThreadPool(); /* 定義 Future 對象,在將來擷取 */ ArrayList> results = new ArrayList>(); for (int i = 0; i < 10; ++i) { /* submit() 方法會產生 Future 對象,可以通過 Future 對象的 isDone() 方法來判斷查詢 Future 是否已經完成,當完成時,可以使用 get() 方法擷取結果 */ results.add(exec.submit(new TaskWithResult(i))); } for (Future fs : results) { try { System.out.println(fs.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } finally { exec.shutdown(); } } }}

優先順序

線程的優先順序將該線程的重要性傳遞給了調度器。調度器會傾向於讓優先順序高的線程先運行。

package concurrency;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * Created by wwh on 16-3-24. */public class SimplePriorities implements Runnable {    private int countDown = 5;    private volatile double d;    private int priority;    public SimplePriorities(int priority) {        this.priority = priority;    }    public String toString() {        return Thread.currentThread() + ": " + countDown;    }    public void run() {        Thread.currentThread().setPriority(priority);        while (true) {            /* 這裡只有迴圈次數比較大才能看出優先順序的優勢 */            for (int i = 0; i < 100000000; ++i) {                d += (Math.PI + Math.E) / (double)i;                if (i % 1000 == 0) {                    Thread.yield();                }            }            System.out.println(this);            if (--countDown == 0) {                return;            }        }    }    public static void main(String[] args) {        ExecutorService exec = Executors.newCachedThreadPool();        for (int i = 0; i < 5; ++i) {            exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));        }        exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));        exec.shutdown();    }}

加入一個線程

一個線程可以在其他線程之上調用 join() 方法,其效果是等待一段時間直到第二個線程結束才繼續執行。join() 調用時可以攜帶逾時參數。

package concurrency;/** * Created by wwh on 16-3-24. */class Sleeper extends Thread {    private int duration;    public Sleeper(String name, int sleepTime) {        super(name);        duration = sleepTime;        start();    }    public void run() {        try {            sleep(duration);        } catch (InterruptedException e) {            e.printStackTrace();            System.out.println(getName() + " was interrupted. " +            "isInterrupted(): " + isInterrupted());            return;        }        System.out.println(getName() + " has awakened");    }}class Joiner extends Thread {    private Sleeper sleeper;    public Joiner(String name, Sleeper sleeper) {        super(name);        this.sleeper = sleeper;        start();    }    public void run() {        try {            /* 等待線程 */            sleeper.join();        } catch (InterruptedException e) {            e.printStackTrace();            System.out.println("Interrupted");        }        System.out.println(getName() + " join completed");    }}public class Joining {    public static void main(String[] args) {        Sleeper                sleepy = new Sleeper("Sleepy", 1500),                grumpy = new Sleeper("Grumpy", 1500);        Joiner                drpey = new Joiner("Dopey", sleepy),                doc = new Joiner("Doc", grumpy);        grumpy.interrupt();    }}
共用受限資源

多個線程可能出現訪問共用資源的情況。

package concurrency;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * Created by wwh on 16-4-7. */public class Counter implements Runnable {    private static int counter = 0;    public static int getCounter() {        return counter;    }    public void run() {        for (int i = 0; i < 1000000; ++i) {            counter++;        }    }    public static void test(int n) {        ExecutorService executorService = Executors.newCachedThreadPool();        for (int i = 0; i < n; ++i) {            executorService.execute(new Counter());        }        executorService.shutdown();    }    public static void main(String[] args) throws InterruptedException {        Counter.test(10);        System.out.println(Counter.getCounter());    }}

以上代碼沒有進行同步,多個線程同時增加計數器。所以導致結果不正確。

Java 為我們提供了幾種方式。

synchronzied:包括兩種用法,synchronzied 方法和 synchronized 塊。對於有 synchronzied 關鍵字修飾的類方法或代碼塊,執行時首先要擷取該類執行個體的鎖,執行完畢後釋放。在執行過程中要有其它線程等請求該 synchronzied 方法或代碼塊則被阻塞。
Lock:互斥鎖,顯示鎖對象。Lock 對象必須被顯式建立、鎖定和釋放。
ReentrantLock:可重新進入鎖,允許嘗試著去擷取鎖。

這幾種方式後續會詳細解釋。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.