Java并发编程之接口ExecutorService的方法使用

7.1 在ThreadPoolExecutor中使用ExecutorService中的方法

方法invokeAny()和invokeAll()具有阻塞特性。
方法invokeAny()取得第一个完成任务的结果值,当第一个任务执行完成后,会调用interrupt()方法将其他任务中断,所以在这些任务中可以结合if(Thread.currentThread().isInterrupted()==true)代码来决定任务是否继续运行。
方法invokeAll()等全部线程任务执行完毕后,取得全部完成任务的结果值。

7.2. 方法invokeAny(Collection tasks)的使用与interrupt

此实验验证方法invokeAny()的确是取得第一个完成任务的结果值,但在这个过程中出现两种情况:
1.无if(Thread.currentThread().isInterrupted())代码:已经获得第一个运行的结果值后,其他线程继续运行。
2.有if(Thread.currentThread().isTnterrupted())代码:已经获得第一个运行的结果值后,其他线程如果使用throw new InterruptedException()代码则这些线程中断,虽然throw抛出了异常,但在main线程中并不能捕获异常。如果想捕获异常,则需要在Callable中使用try-caich显示进行捕获。

public class MyCallableA implements Callable<String> {
  
	@Override
	public String call() throws Exception {
		System.out.println("MyCallableA begin"+System.currentTimeMillis());
		for(int i =0;i<100;i++) {
			Math.random();
			Math.random();
			Math.random();
			System.out.println("MyCallableA"+(i+1));
		}
		System.out.println("MyCallableA end"+System.currentTimeMillis());
		return "returnA";
	}
  
}
.........................
public class MyCallableB1 implements Callable<String>{
	public String call() throws Exception {
		System.out.println("MyCallableB begin"+System.currentTimeMillis());
		for(int i =0;i<200;i++) {
			Math.random();
			Math.random();
			Math.random();
			System.out.println("MyCallableB"+(i+1));
		}
		System.out.println("MyCallableB end"+System.currentTimeMillis());
		return "returnB";
	}
  
}
...............................
public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
	 List list = new ArrayList();
	 list.add(new MyCallableA());
	 list.add(new MyCallableB1());
	 ExecutorService executor = Executors.newCachedThreadPool();
	 //invokeAny 只取得最先完成任务的结果值
	 String getValueA = (String) executor.invokeAny(list);
	 System.out.println("============="+getValueA);
	 System.out.println("ZZZZZZZZZZZZZ");
  }
}
运行结果:
MyCallableB165
MyCallableA end1556285905240
MyCallableB166
MyCallableB167
MyCallableB168
MyCallableB169
MyCallableB170
MyCallableB171
MyCallableB172
MyCallableB173
。。。
MyCallableB187
MyCallableB188
MyCallableB189
MyCallableB190
MyCallableB191
MyCallableB192
=============returnA
MyCallableB193
ZZZZZZZZZZZZZ
MyCallableB194
MyCallableB195
MyCallableB196
MyCallableB197
MyCallableB198
MyCallableB199
MyCallableB200
MyCallableB end1556285905241

invokeAny()取得最先完成任务的结果值,别的线程可以继续执行。

修改B1的程序为B2:

public class MyCallableB2 implements Callable<String> {

	@Override
	public String call() throws Exception {
		System.out.println("MyCallableB begin"+System.currentTimeMillis());
		for(int i =0;i<223456;i++) {
			if(Thread.currentThread().isInterrupted() == false) {
				Math.random();
				Math.random();
				Math.random();
				System.out.println("MyCallableB"+(i+1));
			}else {
				System.out.println("***********抛出异常中断了");
				throw new InterruptedException();
			}
			
		}
		System.out.println("MyCallableB end"+System.currentTimeMillis());
		return "returnB";
	}

}
...................................
public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
	 List list = new ArrayList();
	 list.add(new MyCallableA());
	 list.add(new MyCallableB2());
	 ExecutorService executor = Executors.newCachedThreadPool();
	 //invokeAny 只取得最先完成任务的结果值
	 String getValueA = (String) executor.invokeAny(list);
	 System.out.println("============="+getValueA);
	 System.out.println("ZZZZZZZZZZZZZ");
  }
}
...........................................
运行结果:
。。。。。
MyCallableA12337
MyCallableA12338
MyCallableA12339
MyCallableA12340
MyCallableA12341
MyCallableA12342
MyCallableA12343
MyCallableA12344
MyCallableA12345
MyCallableA end1556286396583
=============returnA
ZZZZZZZZZZZZZ
MyCallableB1536
***********抛出异常中断了

控制台打印的结果说明线程执行完毕后,线程池将线程B设置为中断interrupte状态,而线程B可以自定义对中断interrupted状态进行处理,也就是可以决定是否使用Thread.currentThread().isInterrupted()结合throw new InterruptedException()的代码。
如果使用Thread.currentThread().isInterrupted()结合throw new InterruptedException()的代码说明对线程B进行中断的意图更加明显。

7.3 方法invokeAny(Collection tasks)的使用与interrupt

此实验验证在快的任务优先执行完毕后,执行慢的任务出现异常时,默认情况下不会再控制台输出异常信息。如果显示使用try-catch语句块则可以自定义捕获异常。

public class MyCallableB1 implements Callable<String>{
	public String call() throws Exception {
		System.out.println("MyCallableB begin"+System.currentTimeMillis());
		for(int i =0;i<193456;i++) {
			String s = new String();
			Math.random();
			Math.random();
			Math.random();
			Math.random();
			Math.random();
			System.out.println("MyCallableB"+(i+1));
		}
		if(true) {
			System.out.println("MyCallableB中断了");
			throw new NullPointerException();
		}
		System.out.println("MyCallableB end"+System.currentTimeMillis());
		return "returnB";
	}
  
}
.................................
运行结果:
MyCallableA begin1556287499573
MyCallableB begin1556287499574
MyCallableA1
MyCallableB1
。。。。。。。。。。。
MyCallableA100
MyCallableA end1556287499583
MyCallableB94
。。。。。。。。。。。
MyCallableB179
MyCallableB180
MyCallableB181
=============returnA
ZZZZZZZZZZZZZ

MyCallableB中断了

程序运行的结果:成功取得了字符串returnA,线程B中断了,但抛出的空指针异常却没有在控制台输出。
如果想要在Callable中捕获异常信息,则需要显式地添加try-catch语句块。

public class MyCallableB1 implements Callable<String>{
	public String call() throws Exception {
		System.out.println("MyCallableB begin"+System.currentTimeMillis());
		try {
			for(int i =0;i<200;i++) {
				String s = new String();
				Math.random();
				Math.random();
				Math.random();
				Math.random();
				Math.random();
				System.out.println("MyCallableB"+(i+1));
			}
			if(true) {
				System.out.println("MyCallableB中断了");
				throw new NullPointerException();
			}
			System.out.println("MyCallableB end"+System.currentTimeMillis());
		}catch(Exception e) {
			e.printStackTrace();
			System.out.println(e.getMessage()+"通过显示try-catch捕获异常了");
			throw e;
		}
		
		
		return "returnB";
	}
  
}
...........................
public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
	  try {
		  List list = new ArrayList();
			 list.add(new MyCallableA());
			 list.add(new MyCallableB1());
			 ExecutorService executor = Executors.newCachedThreadPool();
			 //invokeAny 只取得最先完成任务的结果值
			 String getValueA = (String) executor.invokeAny(list);
			 System.out.println("============="+getValueA);
			 System.out.println("ZZZZZZZZZZZZZ");
	  }catch(InterruptedException e) {
		  
		  e.printStackTrace();
		  System.out.println("mainA");
	  }catch(ExecutionException e) {
		  e.printStackTrace();
		  System.out.println("mainB");
	  }
	 
  }
}
运行结果:
MyCallableB198
MyCallableB199
MyCallableB200
MyCallableB中断了
java.lang.NullPointerException
	at Test/cn.yu.executors.executorservice.MyCallableB1.call(MyCallableB1.java:20)
	at Test/cn.yu.executors.executorservice.MyCallableB1.call(MyCallableB1.java:1)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:835)
null通过显示try-catch捕获异常了

从运行结果来看,加入显示的try-catch语句块可以捕获异常信息,但抛出去的异常在main()方法中却没有得到捕获,也就是字符串mainA和mainB没有被打印,也就说明子线程出现异常时是不影响main线程的主流程的。

7.4 方法invokeAny()与执行快的任务异常

此实验验证在执行快的任务出现异常时,在默认情况下是不在控制台输出异常信息的,除非显示使用try-catch捕获,而等待执行慢的任务返回的结果值。

如果想捕获空指针异常使用显示try-catch的方式进行捕获即可,但在MyCallableA的catch块中如果不重新throw抛出异常,则主线程main不能取得MyCallableB任务的返回值,反而取得的是快任务MyCallableA的返回值,也就是MyCallableA的异常状态并未上报,导致线程池认为MyCallableA是正确的,而未发现MyCallableA是一个异常的任务,也就是并未使关注点换到下一个任务,即MyCallableB中。如果MyCallableA将异常再次抛出,则线程池认为MyCallableA出现了异常,则将关注点贴换到MyCallbleB中,取得的返回值是MycalllableB的。

先出现异常而不影响后面任务的取值的原理是在源代码中一直判断有没有正确的返回值,如果直到最后都没有获得返回值则抛出异常,则最终的异常就是在最后出现的异常。比如A,B,C这三个任务一起被执行,都出现了异常,则最终的异常就是最后出现的异常。
此结论可以查看AbstractExecutorService类的方法private T doInvokeAny(Collection<?extends Callable<T //>task,boolean timed,long nanos)

7.5 方法invokeAny()与全部异常

如果全部任务都出现了异常,程序抛出ExecutionException异常

7.6 方法invokeAny(Collection Tasks,timeout,timeUnit)

方法<T>T invokeAny(Collection<?extends Callable<T>tasks,long timeout,TimeUnit unit)的主要作用就是在指定时间内取得第一个先执行完任务的结果值。

如果只有一个任务被运行,这个任务既超时了又出现了异常,运行结果会是什么样子呢?

public class MyCallableA implements Callable<String> {
  
	@Override
	public String call() throws Exception {
		try {
			System.out.println("MyCallableA begin"+System.currentTimeMillis());
			for(int i =0;i<193456;i++) {
				String newString = new String();
				Math.random();
				Math.random();
				Math.random();
				System.out.println("MyCallableA在运行中"+(i+1));
				if(Thread.currentThread().isInterrupted()==true) {
					System.out.println("xxxxxxxxxxxx中断了");
					throw new NullPointerException();
				}
			}
			System.out.println("MyCallableA end"+System.currentTimeMillis());
		}catch(Exception e) {
			e.printStackTrace();
			System.out.println(e.getMessage()+"通过显示try-catch捕获异常了");
			throw e;
			
		}
		
		return "returnA";
	}
  
}
...................................................
public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
	  try {
		     List list = new ArrayList();
			 list.add(new MyCallableA());
			
			 ExecutorService executor = Executors.newCachedThreadPool();
			 //invokeAny 只取得最先完成任务的结果值
			 String getValueA = (String) executor.invokeAny(list,1,TimeUnit.SECONDS);
			 System.out.println("============="+getValueA);
			 
	  }catch(InterruptedException e) {
		  
		  e.printStackTrace();
		  System.out.println("mainA");
	  }catch(ExecutionException e) {
		  e.printStackTrace();
		  System.out.println("mainB");
	  } catch (TimeoutException e) {
		
		e.printStackTrace();
		System.out.println("mainC");
	}
	 
  }
}
..........................
运行结果:
MyCallableA在运行中116015
xxxxxxxxxxxx中断了
java.util.concurrent.TimeoutException
	at java.base/java.util.concurrent.AbstractExecutorService.doInvokeAny(AbstractExecutorService.java:190)
	at java.base/java.util.concurrent.AbstractExecutorService.invokeAny(AbstractExecutorService.java:230)
	at Test/cn.yu.executors.executorservice.Test.main(Test.java:19)
java.lang.NullPointerException
mainC
null通过显示try-catch捕获异常了
	at Test/cn.yu.executors.executorservice.MyCallableA.call(MyCallableA.java:19)
	at Test/cn.yu.executors.executorservice.MyCallableA.call(MyCallableA.java:1)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:835)

异常java.util.concurrent.TimeoutException 和java.lang.NullPointerException同时出现了,其中TimeoutException异常是在main线程中捕获的,而NullPointerException是在Callable中捕获的。

7.7 方法invokeAll(Collection Tasks)

方法invokeAll()会返回所有任务的执行结果,并且此方法执行的效果也是阻塞执行的,要把所有的结果都取回时再继续向下运行。

public class MyCallableA implements Callable<String> {
  
	@Override
	public String call() throws Exception {
		try {
			System.out.println("MyCallableA begin"+System.currentTimeMillis());
//			for(int i =0;i<193456;i++) {
//				String newString = new String();
//				Math.random();
//				Math.random();
//				Math.random();
//				System.out.println("MyCallableA在运行中"+(i+1));
//				if(Thread.currentThread().isInterrupted()==true) {
//					System.out.println("xxxxxxxxxxxx中断了");
//					throw new NullPointerException();
//				}
//			}
			Thread.sleep(5000);
			System.out.println("MyCallableA end"+System.currentTimeMillis());
		}catch(Exception e) {
			e.printStackTrace();
			System.out.println(e.getMessage()+"通过显示try-catch捕获异常了");
			throw e;
			
		}
		
		return "returnA";
	}
  
}
.....................................................
public class MyCallableB2 implements Callable<String> {

	@Override
	public String call() throws Exception {
		System.out.println("MyCallableB begin"+System.currentTimeMillis());
//		for(int i =0;i<223456;i++) {
//			if(Thread.currentThread().isInterrupted() == false) {
//				Math.random();
//				Math.random();
//				Math.random();
//				System.out.println("MyCallableB"+(i+1));
//			}else {
//				System.out.println("***********抛出异常中断了");
//				throw new InterruptedException();
//			}
//			
//		}
		Thread.sleep(8000);
		System.out.println("MyCallableB end"+System.currentTimeMillis());
		return "returnB";
	}

}
...................................................................
public class Test {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
	 
		     List<Callable<String>> list = new ArrayList();
			 list.add(new MyCallableA());
			 list.add(new MyCallableB2());
			 ExecutorService service = Executors.newCachedThreadPool();
			 System.out.println("invokeAll begin"+System.currentTimeMillis());
			 List<Future<String>> listFuture = service.invokeAll(list);
			 System.out.println("invokeAll end"+System.currentTimeMillis());
			 //invokeAny 只取得最先完成任务的结果值
			 for(int i = 0;i<listFuture.size();i++) {
				 System.out.println("返回的结果="+listFuture.get(i).get()+" "+System.currentTimeMillis());
			 }
			 
	  
	 
  }
}
运行结果:
invokeAll begin1556330834933
MyCallableB begin1556330834936
MyCallableA begin1556330834936
MyCallableA end1556330839936
MyCallableB end1556330842936
invokeAll end1556330842936
返回的结果=returnA 1556330842936
返回的结果=returnB 1556330842936

7.8 方法invokeAll(Collection Tasks)快的正确慢的异常

在多个任务的过程中,执行任务快慢与运行时发生的异常也有一些联系

invokeAll()方法对Callable抛出去的异常是可以处理的,由于在main()方法中直接进入了catch语句块,所以导致字符串mainEND也未打印出来。
如果使用invokeAny()方法而某一个任务正确地返回值时,则其他Callable抛出去的异常在main()方法中是不被处理的。
如果使用invokeAny()方法时都没有正确的返回值时,则说明最后Callable抛出去的异常在main()方法中是被处理了的。

7.9 方法invokeAll(Collection Tasks)快的异常慢的正常

快的出现异常后,不在执行慢线程的结果,直接进入到main中的catch语句块

7.10 方法invokeAll(Collection Tasks,long timeout ,timeUnit unit)先慢后快

方法invokeAll(Collection tasks,long timeout,Timeunit unit)的作用是如果全部任务在指定的时间内没有完成,则出现异常。
使用invokeAll()方法出现超时后,调用Future对象的get()方法时出现的是CancellationException异常,而不是invokeAny()方法抛出来的TimeoutException异常

7.10 方法invokeAll(Collection Tasks,long timeout ,timeUnit unit)先快后慢

可以打印快的,慢的报异常

7.10 方法invokeAll(Collection Tasks,long timeout ,timeUnit unit)全慢

一个都不执行,第一个就报异常,后面的无法执行

本章总结:
接口ExecutorService中的方法都以便携的方式去创建线程池,使用两个主要的方法invokeAny()和invokeAll()来取得第一个首先执行完任务的结果值,以及全部任务的结果值。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值