介绍
方法get()结合ExecutorService中的submit(Callable<T>)的使用
方法submit(Callable<T>)可以执行参数为Callable的任务。
方法get()用于获取返回值。
package cn.java.Concurrency;
import java.util.concurrent.Callable;
/**
* @author 小石潭记
* @date 2021/12/19 10:08
* @Description: ${todo}
*/
public class MyCallable implements Callable<String> {
private int age;
public MyCallable(int age) {
this.age = age;
}
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "返回值的年龄是:" + age;
}
}
package cn.java.Concurrency;
import java.util.concurrent.*;
/**
* @author 小石潭记
* @date 2021/12/19 10:09
* @Description: ${todo}
*/
public class Run {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3,
5L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
Future<String> future = executor.submit(myCallable);
System.out.println("main A" + System.currentTimeMillis());
System.out.println(future.get());
System.out.println("main B" + System.currentTimeMillis());
}
}
main A1639880104748
返回值的年龄是:100
main B1639880105759
从结果来看,方法get()具有阻塞特性。
方法get()结合ExecutorService中的submit(Runnable)和isDone()的使用
方法submit()不仅可以传入Callable对象,也可以传入Runnable对象,说明submit方法支持有返回值和无返回值的功能。
package cn.java.Concurrency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @author 小石潭记
* @date 2021/12/19 10:22
* @Description: ${todo}
*/
public class Run1 {
public static void main(String[] args) {
try {
Runnable runnable = () -> System.out.println("打印的信息");
ExecutorService executorService = Executors.newCachedThreadPool();
Future<?> future = executorService.submit(runnable);
System.out.println("结果" + future.get() + "是否完成" + future.isDone());
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印的信息
结果null是否完成true
上面的runnable无返回值,方法get()具有阻塞特性,而isDone方法无阻塞特性。
使用ExecutorService接口的方法submit(Runnable, T result)
该方法的第二个参数result可以作为执行结果的返回值,而不需要使用get()方法来进行获得。
package cn.java.Concurrency;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 小石潭记
* @date 2021/12/19 10:30
* @Description: ${todo}
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfo {
private String username;
private String password;
}
package cn.java.Concurrency;
/**
* @author 小石潭记
* @date 2021/12/19 10:31
* @Description: ${todo}
*/
public class MyRunnable implements Runnable {
private UserInfo userInfo;
public MyRunnable(UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public void run() {
userInfo.setUsername("张三");
userInfo.setPassword("123456");
}
}
package cn.java.Concurrency;
import java.util.concurrent.*;
/**
* @author 小石潭记
* @date 2021/12/19 10:34
* @Description: ${todo}
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
UserInfo userInfo = new UserInfo();
MyRunnable myRunnable = new MyRunnable(userInfo);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3,
5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Future<UserInfo> future = executor.submit(myRunnable, userInfo);
System.out.println("start:" + System.currentTimeMillis());
userInfo = future.get();
System.out.println(userInfo);
System.out.println("end:" + System.currentTimeMillis());
}
}
方法cancel(boolean mayInterruptIfRunning)和isCanacelled()的使用
方法cancel(boolean mayInterruptIfRunning)的参数mayInterrupIfRunning的作用是:如果线程正在运行则是否中断正在运行的线程,在代码中需要使用if(Thread.currentThread().isInterrupted())进行配合。
方法cancel()的返回值代表发送取消任务的命令是否成功完成。
package cn.java.Concurrency;
import java.util.concurrent.*;
/**
* @author 小石潭记
* @date 2021/12/19 10:09
* @Description: ${todo}
*/
public class Run {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3,
5L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
Future<String> future = executor.submit(myCallable);
System.out.println("main A" + System.currentTimeMillis());
System.out.println(future.get());
System.out.println("main B" + System.currentTimeMillis());
System.out.println(future.cancel(true) + " " + future.isCancelled());
}
}
从打印结果来看,线程任务已经运行完毕,线程对象已经销毁,所以cancel方法的返回值是false,代表发送取消的命令并没有成功。如果线程任务没有执行完毕,则调用cancel方法的效果是啥?如下:
package cn.java.Concurrency;
import java.util.concurrent.*;
/**
* @author 小石潭记
* @date 2021/12/19 10:09
* @Description: ${todo}
*/
public class Run {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable(100);
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3,
5L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
Future<String> future = executor.submit(myCallable);
// System.out.println("main A" + System.currentTimeMillis());
// System.out.println(future.get());
// System.out.println("main B" + System.currentTimeMillis());
System.out.println(future.cancel(true) + " " + future.isCancelled());
}
}
任务在没有运行完成之前执行了cancel方法返回true,代表成功发送取消的命令。
前面介绍过参数mayInterruptIfRunning具有中断线程的作用,并且需要结合代码if(Thread.currentThread().isInterrupted())来进行实现。
package cn.java.Concurrency;
import java.util.concurrent.Callable;
/**
* @author 小石潭记
* @date 2021/12/19 11:18
* @Description: ${todo}
*/
public class MyCallable1 implements Callable<String> {
@Override
public String call() throws Exception {
int i = 0;
while (i == 0) {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
System.out.println("正则运行中...");
}
return "我的年龄 100";
}
}
package cn.java.Concurrency;
import java.util.concurrent.*;
/**
* @author 小石潭记
* @date 2021/12/19 10:34
* @Description: ${todo}
*/
public class Test1 {
public static void main(String[] args) throws InterruptedException {
MyCallable1 myCallable1 = new MyCallable1();
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3,
5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Future<String> future = executor.submit(myCallable1);
Thread.sleep(4000);
System.out.println(future.cancel(true) + " "
+ future.isCancelled());
}
}
方法get(long timeout, TimeUnit unit)的使用
在指定的最大时间内等待获取返回值。
自定义拒绝策略RejectedExecutionHandler接口的使用
接口RejectedExecutionHandler的主要作用是当线程池关闭后依然有任务要执行时,可以实现一些处理。
package cn.java.Concurrency;
/**
* @author 小石潭记
* @date 2021/12/19 11:28
* @Description: ${todo}
*/
public class MyRunnable1 implements Runnable {
private String username;
public MyRunnable1(String username) {
this.username = username;
}
@Override
public void run() {
System.out.println(username + "在运行!");
}
}
package cn.java.Concurrency;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author 小石潭记
* @date 2021/12/19 11:26
* @Description: ${todo}
*/
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(((FutureTask) r).toString() + "被拒绝!");
}
}
package cn.java.Concurrency;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author 小石潭记
* @date 2021/12/19 10:34
* @Description: ${todo}
*/
public class Test2 {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3,
5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
executor.setRejectedExecutionHandler(new MyRejectedExecutionHandler());
executor.submit(new MyRunnable1("A"));
executor.submit(new MyRunnable1("B"));
executor.submit(new MyRunnable1("C"));
executor.shutdown();
executor.submit(new MyRunnable1("D"));
executor.submit(new MyRunnable1("E"));
}
}
方法execute()和submit()的区别
方法execute没有返回值,而submit方法可以有返回值。
方法execute在默认情况下异常直接抛出,不能捕获,但可以通过自定义ThreadFactory的方式进行捕获,而submit方法在默认的情况下,可以catch Execution-Exception捕获异常。
参考: JAVA并发编程 核心方法与框架 ,高洪岩著 ,P356