Java闭锁2(Callable接口的模拟与应用)

介绍

Java中一共有4种创建线程的方式,继承Thread、实现Runnable接口、实现Callable接口,通过线程池创建。在以上4种线程的创建方式中,如果你了解果前两种线程的创建方法,那我们是可以模拟实现Callable接口提供的功能的。它旨在通过异步操作之后,主线程能接收异步操作返回的值。笔者在之前的文章中介绍过什么是闭锁,以及如何模拟实现CountDownLatch闭锁,若不知道该内容的同学可以参考笔者之前写的一篇博文,附上地址:https://blog.csdn.net/qq_25956141/article/details/89931707。

原理

一种常见的场景是异步调用多个接口,同时需要等待各个接口都返回成功,主线程才执行成功的代码,否则执行失败的代码。笔者还是以Boss和Worker的例子来简要说明其原理:
Boss下有Worker1,Worker2,Worker3,Boss有一项任务交个他们3人去完成,只有当整个任务都成功,Boss才给他们3发放工资,否则不发放工资。Worker1,Worker2,Worker3决定将任务分成3个部分,并同时开工。Worker1率先完成了工作,给boss说,我已经完成了工作,这时Boss知道还有2个Worker未完成工作,继续等待,当Worker2给Boss说,我已经完成工作了,这是Boss知道还有1个Worker未完成工作,继续等待,直到Worker3给Boss说,我完成了工作内容,但把工作结果失败了。3个Worker都完成了工作,有1个Worker工作失败,导致整个任务失败,Boss决定不给他们发放工资。
如何模拟上述过程?在此之前需要读者先了解闭锁,本文不再做赘述,不了解的读者可以先看笔者之前的博文java闭锁

代码实现

模拟实现

在原理部分,笔者介绍了其原理,接下来将模拟其实现。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

public class MyCallableTest {
	public static void main(String[] args) {
		CountDownLatch latch = new CountDownLatch(3);
		//表示将任务分成3部分,让3个Worker各执一份
		AtomicBoolean task1 = new AtomicBoolean(false);
		AtomicBoolean task2 = new AtomicBoolean(false);
		AtomicBoolean task3 = new AtomicBoolean(false);
		
		Worker1 worker1 = new Worker1(task1, latch);
		Worker2 worker2 = new Worker2(task2, latch);
		Worker3 worker3 = new Worker3(task3, latch);
		
		new Thread(worker1).start();
		new Thread(worker2).start();
		new Thread(worker3).start();
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if(task1.get() && task2.get() && task3.get()){		//都成功才发放工资
			System.out.println("发放工资");
		}else{
			System.out.println("不发放工资");
		}
	}
	
	private static class Worker1 implements Runnable{
		private AtomicBoolean flag = null;
		private CountDownLatch latch = null;
		public Worker1(AtomicBoolean flag,CountDownLatch latch){
			this.flag = flag;
			this.latch = latch;
		}
		@Override
		public void run() {
			try {
				Thread.sleep(1000);			//模拟工作时间
				flag.getAndSet(true);		//第一个工人工作成功
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				latch.countDown();
			}
		}
	}
	
	private static class Worker2 implements Runnable{
		private AtomicBoolean flag = null;
		private CountDownLatch latch = null;
		public Worker2(AtomicBoolean flag,CountDownLatch latch){
			this.flag = flag;
			this.latch = latch;
		}
		@Override
		public void run() {
			try {
				Thread.sleep(2000);				//模拟工作时间
				flag.getAndSet(false);			//第2个工人工作失败
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				latch.countDown();
			}
		}
	}
	
	private static class Worker3 implements Runnable{
		private AtomicBoolean flag = null;
		private CountDownLatch latch = null;
		public Worker3(AtomicBoolean flag,CountDownLatch latch){
			this.flag = flag;
			this.latch = latch;
		}
		@Override
		public void run() {
			try {
				Thread.sleep(3000);				//模拟工作时间
				flag.getAndSet(true);			//第3个工人工作成功
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				latch.countDown();
			}
		}
	}
}

输出结果:不发放工资

以上主要通过一个原子类AtomicBoolean原子类模拟其实现,当3个task的值都为真时,整个任务成功,否则失败。

使用Callable接口实现

在JUC中提供了Callable接口简化了上述操作,写法与上述相似。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;


public class CallableTest {	//Boss
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		Worker1 worker1 = new Worker1();		//3个工人
		Worker2 worker2 = new Worker2();
		Worker3 worker3 = new Worker3();
		//将任务拆分成3个部分
		FutureTask<Boolean> task1 = new FutureTask<Boolean>(worker1);
		FutureTask<Boolean> task2 = new FutureTask<Boolean>(worker2);
		FutureTask<Boolean> task3 = new FutureTask<Boolean>(worker3);
		
		new Thread(task1).start();
		new Thread(task2).start();
		new Thread(task3).start();
		
		if(task1.get()&& task2.get() &&task3.get()){	//get是一个阻塞方法,未完成,则不会继续执行
			System.out.println("发放工资");
		}else{		//有一个或者几个不成功,不发放工资
			System.out.println("不发放工资");
		}
	}
	
	private static class Worker1 implements Callable<Boolean>{
		@Override
		public Boolean call() throws Exception {
			Thread.sleep(1000);		//模拟工作时间
			return true;			//第1个工人工作成功
		}
	}
	
	private static class Worker2 implements Callable<Boolean>{
		@Override
		public Boolean call() throws Exception {
			Thread.sleep(2000);		//模拟工作时间
			return false;			//第2个工人工作失败了
		}
	}
	
	private static class Worker3 implements Callable<Boolean>{
		@Override
		public Boolean call() throws Exception {
			Thread.sleep(3000);		//模拟工作时间
			return true;			//第3个工人工作成功
		}
	}
}

输出结果:不发放工资

JUC提供的Callable接口大大简化了其操作,其需要一个FutureTask维护其操作。

总结

本文介绍了闭锁的另一种实现方法,即Callable接口的模拟与应用,同时用一个Boss和Worker的例子来说明其原理,最后给出了代码实现和输出结果。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值