获取线程的执行结果
一: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(); } }
好了,到此,本篇文章就结束了。