Java线程类三

一、Callable

最早创建线程要么是通过实现Runnable接口,或者是继承Thread类来实现(Thread类本身是Runnable的一个实现类),但是都有一个问题:不能携带返回值。

 

从Java 5开始,提供了一个Callable接口,可以用来提供带返回值的线程,例如:

class CallableDemo implements Callable<String>{

	@Override
	public String call() throws Exception {
		return "Hello World";
	}
	
}

Callable类似于Runnable,只不过对应的方法名是call(),而不是run(),另外不同与run()方法,call()方法需要指定一个返回值,其中的返回类型由声明实现时的Callable中的GenericType指定。要调用一个Callable不能像Runnable一样简单的使用Thread去启动(看看Thread就知道,它根本就不支持使用Callable)。

有几种方法可以启动一个Callable:

  1. FutureTask
    FutureTask可以接收一个Runnable也可以接收一个Callable,从类继承层次:FutureTask -> RunnableTask -> Runnable 可以看出,FutureTask可能入在一个Thread中,所以可以如下启动用一个调用Callable实现的线程:
    		FutureTask<String> futureTask = new FutureTask<String>(new CallableDemo());
    		new Thread(futureTask).start();
    		String response = futureTask.get();
    		System.out.println(response);
    将Callable放在一个FutureTask中,然后将此FutureTask放在一个Thread中启动;调用FutureTask的get()方法用于得到返回值,此方法会一直阻塞,直到某个异常发生或者线程调用结束,返回值返回。 
  2. Executors
    Executors是一个工厂类,提供了一些通用方法来创建各种线程相关对象,例如将一个Runnable转成Callable;创建一个线程池(ThreadPool/ExecutorService);创建一个线程工厂(ThreadFactory)等。其中ExecutorService类可以用来启动一个Callable线程,例如:
    		CallableDemo callable = new CallableDemo();
    		ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    		Future<String> future = singleThreadExecutor.submit(callable);
    		String response = future.get();
    		System.out.println(response);
    这里用newSingleThreadExecutor()方法是因为只有一个Callable对象要调用。可以换成其他的,例如newFixedThreadPool(int)或newCachedThreadPool()方法,如果还有更多的Callable需要调用。同样的用get()方法得到返回值。
上面用get()方法取返回值,如果线程没结束则会一直阻塞,直到线程取消或结束。如果要想判断当前线程的状态,也可以用isDone()和isCancelled()方法查询当前的状态,然后根据状态再取结果。
不过如果有大量的线程执行,那么一个一个查就不太方便了,这就可以使用下面介绍的ExecutorCompletionService类了。

二、ExecutorCompletionService

上面说了为了得到线程运行结果,需要查询线程的状态,调用future的get()方法来得到结果,如果线程未结束,则get()方法阻塞。如果有大量的线程在运行,则必须一个一个查询这些线程的状态以得到返回结果,针对此,Java刚好提供了一个ExecutorCompletionService类可以用来做这种查询,ExecutorCompletionService类会把所有运行完的线程放到一个有序队列里去,只要调用它的take()方法,就会从中把结果一个一个取出。
首先修改一下上例中的CallableDemo:
class CallableDemo implements Callable<String>{

	private int i;
	private CountDownLatch latch;

	public CallableDemo(int i, CountDownLatch latch) {
		this.i = i;
		this.latch = latch;
	}

	@Override
	public String call() throws Exception {
		latch.await();
		System.out.println(i);
		return "Hello World "+i;
	}
}
增加了一个变量i,用于区别不同的线程;增加了一个变量latch用于在所以线程准备好之前等待。
然后看看创建端:
		CountDownLatch latch = new CountDownLatch(10); //设置门槛为20
                //创建一个线程数为5的线程池
		ExecutorCompletionService<String> completionService = new ExecutorCompletionService<String>(Executors.newFixedThreadPool(5));
                //创建20个callable对象,并用completionService去启动它
		for(int i = 0;i<10;i++){
			completionService.submit(new CallableDemo(i, latch));
			latch.countDown();
		}
                //从completionService中取结果
		for(int i = 0;i<10;i++){
			Future<String> take = completionService.take(); //如果没有结果,则等待
			System.out.println(take.get());
		}
在Callable中打印了一下i,是想用来比较一下是不是先完成先被加到完成队列里。下面是某次的运行结果:
4
0
2
1
3
8
7
6
5
Hello World 0
9
Hello World 4
Hello World 2
Hello World 1
Hello World 3
Hello World 8
Hello World 7
Hello World 6
Hello World 5
Hello World 9
可以看到基本上先执行的先被加到队列里(4和0可能基本是同时结束)。   

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值