獲取線程的執行結果

一:Runnable和Callable的區別

最本質的區別在於,Runnable沒有返回結果,Callable會有一個返回結果,返回結果是泛型,可以自己定義。舉例子說明:

public class ThreadRunnable {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyRunnable runnable = new MyRunnable();
        new Thread(runnable).start();

        MyCallable callable = new MyCallable();
        FutureTask task = new FutureTask(callable);
        new Thread(task).start();
        System.out.println("callable: " + task.get());
    }
}

class MyRunnable implements Runnable {

    @Override
    public void run() {
        return;
    }
}

class MyCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        return "hello";
    }
}

 

      上述例子中可以看到,callable可以定義一個返回結果,通過FutureTask的get方法可以獲得線程執行後的結果(阻塞等待結果)。

源碼查看:

/**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

可以看到,Thread裏可以放Runnable,但是不可以放callable,繼續查看FutureTask:

public class FutureTask<V> implements RunnableFuture<V> {
    /*
     * Revision notes: This differs from previous versions of this
     * class that relied on AbstractQueuedSynchronizer, mainly to
     * avoid surprising users about retaining interrupt status during
     * cancellation races. Sync control in the current design relies
     * on a "state" field updated via CAS to track completion, along
     * with a simple Treiber stack to hold waiting threads.
     *
     * Style note: As usual, we bypass overhead of using
     * AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
     */

    /**
     * The run state of this task, initially NEW.  The run state
     * transitions to a terminal state only in methods set,
     * setException, and cancel.  During completion, state may take on
     * transient values of COMPLETING (while outcome is being set) or
     * INTERRUPTING (only while interrupting the runner to satisfy a
     * cancel(true)). Transitions from these intermediate to final
     * states use cheaper ordered/lazy writes because values are unique
     * and cannot be further modified.
     *
     * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;
}
/**
 * A {@link Future} that is {@link Runnable}. Successful execution of
 * the {@code run} method causes completion of the {@code Future}
 * and allows access to its results.
 * @see FutureTask
 * @see Executor
 * @since 1.6
 * @author Doug Lea
 * @param <V> The result type returned by this Future's {@code get} method
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

上面的代碼看到了:最終FutureTask也是實現了Runnable。

二:自定義一個FutureTask類

FutureTask的get可以阻塞等待結果,那麼在寫之前思考下,需要哪些步驟?

步驟如下:

1:需要去重寫繼承的Runnable接口的run方法;

2:需要一個callable變量,用來執行call()方法;

3:需要一個泛型的變量,用來存放結果;

4:還需要一個隊列,用來存放那些阻塞還未返回結果的線程;

5:需要一個標誌,判斷線程是否執行完,可以返回結果;

6:最後,需要一個獲取結果的方法。

代碼實現如下,有註釋可以方便查看:

public class ThreadFutureTask<T> implements Runnable {

    //判斷線程有沒有執行完,可以返回結果
    public static volatile boolean isEnding = false;

    //callable對象
    public Callable<T> callable;

    //線程執行後的結果
    public volatile T result;

    //隊列,用於存放未執行完的線程,即阻塞的線程,放到隊列中
    public LinkedBlockingDeque<Thread> waiter = new LinkedBlockingDeque<>();

    //構造函數
    public ThreadFutureTask(Callable<T> callable) {
        this.callable = callable;
    }

    //實現Runnable的run方法
    @Override
    public void run() {
        try {
            //獲取callable執行的結果
            result = callable.call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //最後,不管有沒有執行完,都結束獲取結果
            isEnding = true;
        }

        //把隊列中阻塞的線程,喚醒,否則獲取不到結果
        while (true) {
            Thread thread = waiter.poll();
            //要是隊列中沒有線程在掛起,就直接返回
            if (thread == null) {
                break;
            }
            //喚醒掛起的線程
            LockSupport.unpark(thread);
        }

    }

    //獲取結果的方法
    public T get() {
        //如果線程執行完了,就獲取結果,否則就掛起
        if (!isEnding) {
            waiter.offer(Thread.currentThread());
        }
        //讓線程掛起
        while (!isEnding) {
            LockSupport.park();
        }
        //返回結果
        return result;
    }
}

然後再去上面的runnable和callable區別的測試類裏測試一下:

 public static void main(String[] args) throws ExecutionException, InterruptedException {
//        MyRunnable runnable = new MyRunnable();
//        new Thread(runnable).start();

        MyCallable callable = new MyCallable();

        ThreadFutureTask task = new ThreadFutureTask(callable);
//        FutureTask task = new FutureTask(callable);
        new Thread(task).start();
        System.out.println("callable: " + task.get());
}

返回結果如下:

可以看到,返回結果和FutureTask一樣,這樣就實現了FutureTask的功能。

 三:CountDownLatch

      CountDownLatch是一個同步工具類,它是讓一個或者多個線程等待,直到其他線程執行完再執行;CountDownLatch是通過倒計數器來實現的,它的初始值就是線程的數量,每當一個線程執行完畢,計數器就減一,直到減到0,輸出所有線程執行的結果,等待的線程就可以恢復繼續執行。

public static AtomicLong num = new AtomicLong(0);

    public static void main(String[] args) throws InterruptedException {

        //倒計時計數器,
        CountDownLatch latch = new CountDownLatch(10);
        for (int i=0; i<10; i++) {
            new Thread(() -> {
                for (int j=0; j<100000; j++) {
                    num.getAndIncrement();
                }
                //計數器減1
                latch.countDown();
            }).start();
        }
        //打開開關
        latch.await();
        System.out.println(num.get());
}

四:Semaphore

Semaphore是一個計數信號量,可以控制訪問資源的線程數量,也就是說,它是一個可以控制併發的共享鎖,也就是限流。

  public static void main(String[] args) {

        //初始值就是可以通過的數量
        Semaphore semaphore = new Semaphore(2);

        for (int i=0; i<100; i++) {
            new Thread(() -> {
                try {
                    //拿到通道
                    semaphore.acquire();
                    System.out.println("開始通過橋樑。。。。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                test("222");
                //執行完釋放通道
                semaphore.release();
            }).start();
        }
    }

    public static void test(String name) {
        System.out.println(name + "成功通過");
        LockSupport.parkNanos(1000 * 1000 * 1000 * 3000);
    }

五:CyclicBarrier

CyclicBarrier循環柵欄,可以循環利用的屏障,它的作用就是讓線程都等待完成後纔會再去執行。

比如:去遊樂園做摩天輪,假設只能上齊四個人才允許啓動,那麼再沒有上齊之前,其他人就在等待。

public static void main(String[] args) {

        //初始值就是每次可以處理的數量
        CyclicBarrier barrier = new CyclicBarrier(2);

        for (int i=0; i<100; i++) {
            new Thread(() -> {
                try {
                    //等待
                    barrier.await();
                    System.out.println("摩天輪啓動了。。。。");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }

}

好了,到此,本篇文章就結束了。 


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章