[java]FutureTask手撕大厂多线程面试题

某大型电商Java面试题:一主多从多线程协作 问:客户请求下单服务(OrderService),服务端会验证用户的身份(RemotePassportService), 用户的银行信用(RemoteBankService),用户的贷款(RemoteLoanService)。为提高并发效率,要求三项服务验证工作同时进行,如其中任意一项验证失败,则立即返回失败,否则等待所有验证结束,成功返回。要求Java实现。

目标

  • 多线程
  • 验证失败,立即返回

主要技术点:

  • .FutureTask的get()方法是阻塞式方法跟CountDownLauch原理差不多都是闭锁,FutureTask的cancel()可以中断其余线程

主要思路

  • 三个线程并发执行对应的校验程序,如果有一个校验失败,调用Check类的fail函数,结束其他进程。
  • 当前台遍历tasks的时候,如果get方法抛出中断异常说明已经校验失败。如果没有调用过Check类的fail函数说明校验成功。
public class FutureTask2 {
	public static void main(String[] args) {
		List<FutureTask<Boolean>> tasks = new ArrayList<FutureTask<Boolean>>();
		FutureTask<Boolean> task1 = new FutureTask<Boolean>(new RemotePassportService());
		FutureTask<Boolean> task2 = new FutureTask<Boolean>(new RemoteBankService());
		FutureTask<Boolean> task3 = new FutureTask<Boolean>(new RemoteLoanService());
		tasks.add(task1);
		tasks.add(task2);
		tasks.add(task3);
		Check.setTasks(tasks);
		new Thread(task1).run();
		new Thread(task2).run();
		new Thread(task3).run();
		//循环的get一下,目的是为了确保Check.isFailed方法在所有校验服务完成之后执行
		//因为get是阻塞式方法 如果后面有校验失败的其他线程就会cancel,如果get不到就会报错
		//如果抛出异常说明已经某项校验失败了
		for(FutureTask<Boolean> task:tasks){
			try {
				task.get();
			} catch (Exception e) {break;}
		}
		if(Check.isFailed) System.out.println("校验失败");
		else{System.out.println("校验成功");}
	
}
}

class RemotePassportService implements Callable<Boolean>{
	Random r = new Random();
	Check c;

	public Boolean call() throws Exception {
		Boolean flag = r.nextBoolean();//作为测验值
		if(flag){System.out.println("证件校验成功");return true;}
		else{
			System.out.println("证件校验失败");
			return Check.fail();
	}
	}
}
//银行校验
class RemoteBankService implements Callable<Boolean>{
	Random r = new Random();
	Check c;

	public Boolean call() throws Exception {
		Boolean flag = r.nextBoolean();
		if(flag){System.out.println("银行校验成功");return true;}
		else{
			System.out.println("银行校验失败");
			return Check.fail();
	}
	}
}
//贷款校验
class RemoteLoanService implements Callable<Boolean>{
	Check c;
	Random r = new Random();

	public Boolean call() throws Exception {
		Boolean flag = r.nextBoolean();
		if(flag){System.out.println("贷款校验成功");return true;}
		else{
			System.out.println("贷款校验失败");
			return Check.fail();
	}
}
}


class Check{
	static List<FutureTask<Boolean>> tasks;
	public static Boolean isFailed = false;
	public static Boolean fail(){
		isFailed = true;
		for(FutureTask<Boolean> task:tasks){
			task.cancel(true);
		}
		return false;
	}
	public Boolean getIsFailed() {
		return isFailed;
	}
	public static void setTasks(List<FutureTask<Boolean>> tasks) {
		Check.tasks = tasks;
	}
	
}

2019/10/8更新

这个demo可能不太好理解

其实循环遍历get是为了保证主线程的Check.isFailed在子线程执行完后执行。因为FutureTask的get方法是阻塞式方法。如果某个线程检验失败,那么会调用Check.fail()方法,取消其他的校验工作,当主线程在get的时候,如果捕获到中端异常,说明某一项校验失败。直接退出,打印校验失败

关于是否其中任意一项验证失败,则立即返回失败,答案是是的

当调用线程的run方法时,三个线程同时执行,如果某个线程校验失败。就会使全部的线程取消执行。前台get到的就会抛出中端异常。并不是集合中get到的第一个先执行。假设第二个线程首先执行完校验失败,会取消全部执行的线程,此时get到的第一个会抛出中端异常,打印校验失败

所以这个demo是完全符合要求的!!!

你可能感兴趣的:(guc并发)