Callable实现JAVA多线程

最近项目用到一个功能需要实现多线程分发任务且需要任务的返回值,之前一直都是实现Runnable接口,但里面的run方法是返回void的。后来在网上查了下JAVA1.5开始就有了Callable。

下面来看看如何倒腾下这个东西。

import java.util.concurrent.Callable;

/**
 * @类说明 线程业务处理
 * @author DavenTsang
 * @date 2016-11-16
 * 
 */
public class PoolTask implements Callable<String> {

    private String id;

    @Override
    public String call() throws Exception {
        return "当前线程名:" + Thread.currentThread().getName() + ":" + id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}
View Code

先建立一个类实现Callable接口。一下是JDK的API对Callable接口的描述:

public interface Callable<V>

返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。

Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。

Executors 类包含一些从其他普通形式转换成 Callable 类的实用方法。

V是需要返回的对象。

需要执行这个实现类,我们需要创建一个线程池

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class  PoolUtils {

        //可伸缩线程池
        private static ExecutorService cachedPool = Executors.newCachedThreadPool();
        public static CompletionService<String> completionService = new ExecutorCompletionService<String>(cachedPool);
        private PoolTask task;
        public String addTask() throws InterruptedException, ExecutionException{
            //添加任务
            Future<String> future = completionService.submit(task);
            //检查是否出现第二个线程进来
            Thread.sleep(1000);
            List<Future<String>> list = new ArrayList<Future<String>>();
            System.out.println(completionService.take().get());
            list.add(future);
            return completionService.take().get();
        }
        
        public PoolTask getTask() {
            return task;
        }
        public void setTask(PoolTask task) {
            this.task = task;
        }
        
}
View Code

Thread.sleep();调用这个方法是因为在前面添加任务是用一个线程数组调用,看下Executors.newCachedThreadPool();这个是否可以自己去根据需要创建线程。

我们再来看下submit()方法的源码,

public Future<V> submit(Callable<V> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<V> f = newTaskFor(task);
        executor.execute(new QueueingFuture(f));
        return f;
    }
  private class QueueingFuture extends FutureTask<Void> {
        QueueingFuture(RunnableFuture<V> task) {
            super(task, null);
            this.task = task;
        }
        protected void done() { completionQueue.add(task); }
        private final Future<V> task;
    }

 追一下源码会发现里面有个队列存在。submit添加一个任务就会放到队列里面。这样就不用我们显示的去创建一个队列。

调用类:

import java.util.concurrent.ExecutionException;

public class Test {
    
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
            Thread[] thread = new Thread[10];
            for(int i = 0;i<thread.length;i++){
                thread[i] = new Thread(new A());
            }
            for(Thread th : thread){
                th.start();
            }
        
        
    }
}

class A implements Runnable{
    @Override
    public void run() {
        PoolTask task = new PoolTask();
        task.setId("daven");
        PoolUtils utils = new PoolUtils();
        utils.setTask(task);
        try {
            String a = utils.addTask();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
View Code


调用类用多个线程去调用是因为模拟项目的场景了。

以上是一些记录供自己回忆用。遇到问题先自己查找原因,再去网上找下然后再找API追源码。

原文地址:https://www.cnblogs.com/davenzeng/p/6073048.html