获取线程的执行结果

一: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 {

    @Overridepublic void run() {return;
    }
}class MyCallable implements Callable<String> {

    @Overridepublic 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可以阻塞等待结果,那么在写之前思考下,需要哪些步骤?

获取线程的执行结果_线程_02

步骤如下:

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方法    @Overridepublic 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();
        }

}

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