异步和同步
异步
- 异步的含义是计算机多线程的异步处理。与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程,获取此线程的结果。
- 但此处需要明确的是:异步与多线程与并行不是同一个概念。
同步的时候无法异步,但异步的过程中可以有同步
并发的执行时间间隔中,也可能会变成并行
同步
- 线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
- 当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
- 线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
Java
并发包中的Future
接口实现异步
何谓Future
接口
见名知意:在(future)未来会完成任务,在未来(future)获得任务的结果
- Future表示异步计算的结果。 提供了检查计算是否完成、等待计算完成以及检索计算结果的方法。
- 结果只能在计算完成后使用
get
方法检索,必要时阻塞,直到它准备好。 - 取消由
cancel
方法执行。 提供了其他方法来确定任务是正常完成还是被取消。 一旦计算完成,就不能取消计算。 如果为了可取消性而想使用Future但不提供可用结果,则可以声明Future<?>形式的类型并返回null
作为底层任务的结果。
看看Future
接口中的方法
见名知意,不赘述了,就看看
get()
方法即可
V get()
方法
- 如有必要,等待计算完成,然后检索其结果。
- 返回值:计算结果
V get(long timeout, TimeUnit unit)
方法
- 如有必要,最多等待给定的计算完成时间,然后检索其结果(如果可用)。
- 参数:
- timeout – 等待的最长时间
- unit – 超时参数的时间单位
- 返回值:计算结果
- 抛出:
- CancellationException – 如果计算被取消
- ExecutionException – 如果计算抛出异常
- InterruptedException – 如果当前线程在等待时被中断
- TimeoutException – 如果等待超时
既然是接口就有实现类
132个实现类!!!
选FutureTask<V>
进行讲解
- 成员变量和构造方法
关于FutureTask<V>
类的样例
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> callable = ()->{
System.out.println("pre Task");
int randomNumber = new Random().nextInt(500);
Thread.sleep(2000);
System.out.println("post Execution");
return randomNumber;
};
FutureTask<Integer> futureTask = new FutureTask<>(callable);
new Thread(futureTask).start();//FutureTask继承了Runable接口,所以可以作为参数
System.out.println("Thread has start");
System.out.println(futureTask.get());//会导致主线程阻塞,因为sleep 2000
System.out.println("main over");
}
}
复制代码
但是Future
接口有缺点
- 它的get()方法,如果异步任务没有完成就调用get()方法会导致线程阻塞
- 虽然get方法有一个重载的超时参数的方法,但是,如果任务超时就不能再获取结果了,而会抛出异常
- 在实际开发中较少使用
使用CompletableFuture
来避免Future
接口的缺点
随意举一些CompletableFuture
的样例
- 代码
public class CompletableFutureT {
public static void main(String[] args) throws InterruptedException {
String result = CompletableFuture
.supplyAsync(() -> "hello") //提供
.thenApplyAsync(value -> value + " world")//应用
.join();//完成时返回结果
System.out.println(result);
System.out.println("=========分割线========");
CompletableFuture
//提供,参数是Supplier,不接收参数返回结果,
.supplyAsync(() -> "Kitty Guy")
//然后消费,参数是Consumer,接收参数数不返回结果
.thenAcceptAsync(value -> System.out.println("welcome " + value));
Thread.sleep(2);//貌似上面的操作有有点慢
System.out.println("=========分割线========");
String result2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello";
})
.thenCombine(CompletableFuture.supplyAsync(() -> " world"), (s1, s2) -> s1 + s2)
.join();
System.out.println(result2);
}
}
复制代码
- 下面的是样例中用到的方法
CompletableFuture
解决Future
中get
方法的阻塞问题样例
- 代码
public class CompletableFutureT {
public static void main(String[] args) throws InterruptedException {
Runnable runnable =()->{
try {
Thread.sleep(2000);
System.out.println("Runable 睡眠结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
CompletableFuture<Void> completableFuture2 = CompletableFuture.runAsync(runnable);//任务1
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(()->{//任务2
try {
Thread.sleep(1000);
System.out.println("supplier 睡眠结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "来自异步任务的字符串";
});
//回调,当任务完成就执行whenComplete参数里面的方法
completableFuture.whenComplete(((res, throwable) -> System.out.println(res+" 任务执行完") ));
completableFuture2.whenComplete(((res, throwable) -> System.out.println("Runable 任务执行完") ));
System.out.println("主线程结束");//会直接输出,不会阻塞
Thread.sleep(5000);//需要主线程存活,不然异步任务会被关闭
}
}
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END