java Executor框架

1.为什么要使用线程池

  • 单线程执行任务必须串行的执行,那么如果某个任务需要很长的计算时间或者发生IO等阻塞,那么后面的任务必须长时间的等待。并且在多处理器环境中,单线程只占用一个处理器,其他处理器得不到利用。
  • 假设为每一个任务创建一个线程(即无限制的创建线程),任务执行完之后销毁线程。那么频繁的创建和销毁线程需要很大的开销;并且当线程的数量远大于处理器的数量时,大量的线程需要在内存中等待并占用内存;随着内存中等待线程数量的增多,最终会耗尽内存。
  • 而线程池是以上两种情况的折中。线程池中维护着一定数量的线程,每个任务获取到一个空闲的线程,任务执行完毕后这个线程将会被再次利用,当没有空闲线程时,任务必须等待。这样既提高了CPU的利用率,又减少了内存消耗。
  • 当所有任务的执行执行时候都很短并且在单处理器环境中时,使用单线程策略是合理的。

2.Executor框架

Executor框架的思路就是将任务的提交与执行分开,具体的执行策略可以选择不同的线程池:如单线程,多线程等。
这里写图片描述

2.1 Executor接口
public interface Executor {
    void execute(Runnable command);
}

Executor接口是整个Executor框架的基础,它提供了一种标准的方法来将任务的提交与执行分开,并用Runnable来表示任务。

 Executor executor = anExecutor; //创建一个任务的执行者
 executor.execute(new RunnableTask1());//将任务提交给Executor,具体的执行方法由Executor决定
 executor.execute(new RunnableTask2());

Executor处理任务的方法是由execute方法决定而不是任务的提交线程,执行的策略可以是由提交任务的线程执行,创建一个线程执行,交个一个单线程执行,交给线程池等等。

//交给提交线程执行:
class DirectExecutor implements Executor {
   public void execute(Runnable r) {
     r.run();
   }
}
//另外创建线程执行:
 class ThreadPerTaskExecutor implements Executor {
   public void execute(Runnable r) {
     new Thread(r).start();
   }
 }
2.2 ExecutorService接口
  • Executor接口的问题:虽然将任务提交与执行分开了,但是任务提交之后,任务采用一定策略被执行直到所有的任务都执行完毕,那么Executor的生命周期也就结束了。但是提交线程无法查看任务的执行情况(成功执行还是发生了错误),并且Executor的生命周期也得不到管理。
  • ExecutorService接口扩展了Executor接口,它提供了管理线程池的方法以及查看任务执行情况的方式。ExecutorSecvice的生命周期是运行,关闭,已终止。
interface ExecutorService extends Executor {
    void shutDown(); 
    List<Runnable> shutDownNow(); 
    boolean awaitTermination(long timeout, TimeUnit unit);
    Future<T> submit(Callable<T> task);
    Future<?> submit(Runnable task);
    Future<T> submit(Runnable task, T result);
    List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
    ....
}
  • shutDown采用平缓的关闭过程,不在接受提交任务,但是已经提交的任务会继续执行直到结束。
  • shutDownNow采用强硬的关闭过程,不接受提交任务并且尝试取消所以已提交但是没有运行结束的任务,并且返回已经提交但是还没有开始执行的任务。
  • awaitTermination阻塞直到任务全部完成或者时间超时,通常调用之后会接着调用shutDown,因为任务没有在你期望的时间内结束,所以需要终止他们。
  • submit类似于excute方法,只是使用submit提交任务之后会返回一个用于查看任务执行情况的接口Future(立即返回)。
  • invokeAll用于批量提交任务,但是与submit不同的是,它需要阻塞直到所有的任务都执行完毕才会返回Future队列。此外还可以设置阻塞时间(阻塞时间到了之后将返回执行完的任务Future,未完成的任务都将取消)。
2.3 Callable和Future
  • Callable和Runnable非常相似,不同之处在于Runnable不能抛出异常及返回结果但是Callable可以。所以可以用Callable执行一段任务最后返回一个执行结果。
public interface Callable<V> {
    V call() throws Exception;
}
  • Future是提交任务之后返回的用于查看任务执行结果的接口。
  • cancel(true)用于取消任务,isCancelled()用于判断任务是否取消
  • isDone()用来判断任务是否已完成
  • get()方法的行为取决于任务的状态,如果任务已经完成,那么他将立即返回执行结果或者抛出异常,如果任务没有完成,他将阻塞直到任务完成。如果任务被取消,将抛出CancellationException。但是在invoke方法中,isDone一定是返回true。
  • 如果使用Runnable作为任务提交的方式并且没有设置返回结果,那么get会得到null。
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get();
    V get(long timeout, TimeUnit unit);
}

此外,可以使用FutureTask显示的提交Runnable或者Callable任务。因为FutureTask实现了RunnableFuture,而RunnableFuture继承了Runnable和Future接口,因此FutureTask可以使用executor.execute(futureTask)提交。

FutureTask<String> future =
   new FutureTask<String>(new Callable<String>() {
     public String call() {
       return searcher.search(target);
   }});
 executor.execute(future);
2.4 ScheduledExecutorService接口
  • ScheduledExecutorService接口继承并扩展了ExecutorService接口。可以用来提交延迟和周期任务。
  • ScheduledExecutorService接口主要扩展了四个方法,schedule用来设置延迟任务,scheduleAtFixedRate和scheduleWithFixedDelay用来设置周期任务。
  • ScheduledExecutorService使用的时间都是相对时间而非绝对时间。
  • scheduleAtFixedRate的执行时间依次是: initialDelay,initialDelay+period, initialDelay + 2 * period, and so on。
  • scheduleWithFixedDelay第一次执行的时间是initialDelay,之后每次执行时间是上一次执行结束后等待period时间,然后执行。
  • 未执行完成的任务可以通过cancel取消,对于周期任务,如果某个任务抛出异常,那么后续任务不在执行。否则只有通过cancel以及关闭服务来取消。
public interface ScheduledExecutorService extends ExecutorService {
    ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
    ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
    ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
    ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
    ....
}
Executors工厂

Executors用来实例化各种类型的Executor(ExecutorService,ScheduledExecutorService)。

class Executors {
    static ExecutorService newCachedThreadPool();
    static ExecutorService newFixedThreadPool(int nThreads);
    static ExecutorService newSingleThreadExecutor();
    static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
    static ScheduledExecutorService newSingleThreadScheduledExecutor();
    .....
}
  • newCachedThreadPool:创建一个可缓存的线程池,如果线程空闲时间超过sixty seconds就会被销毁,当没有空闲线程可以执行新的任务时会创建新的线程,因此线程数量会动态变化,并且线程的规模没有限制。(不需要任务等待队列)
  • newFixedThreadPool:创建一个线程数量固定的线程池,如果某个线程抛出Exception而意外终止,会创建一个新的线程来维持线程的数量固定。当有新的任务到来并且没有空闲的线程时,任务会进入一个无边界的队列中等待。
  • newSingleThreadExecutor:创建一个单线程的Executor,如果这个线程异常结束,会创建一个新的线程替代它,他能保证提交的任务串行执行(FIFO,LIFO,优先级)。
2.4 CompletionService && ExecutorCompletionService

CompletionService接口的作用是将Executor的任务执行与结果返回分离,由CompletionService来管理任务执行完毕后返回的结果。所有已完成的结果会加入到CompletionService维护的阻塞队列当中,用户只需要反复调用take方法来取回执行结果,ExecutorCompletionService实现了接口CompletionService。

public class ExecutorCompletionService implements CompletionService {
    ExecutorCompletionService(Executor executor);
    ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue);
    Future<V> poll();
    Future<V> poll(long timeout, TimeUnit unit);
    Future<V> take();
    Future<V> submit(Callable<V> task);
    Future<V> submit(Runnable task, V result);
}
  • take()方法从阻塞队列中取出一个任务执行结果,如果没有执行完成的结果则会阻塞。
  • poll()方法从阻塞队列中取出一个任务执行结果,如果没有执行完成的结果则会返回null,不会阻塞。
2.5 ThreadPoolExecutor和ScheduledThreadPoolExecutor自定义线程池
  • 除了使用Executors工厂实例化线程池,还可以使用ThreadPoolExecutor和ScheduledThreadPoolExecutor这两个类来自定义线程池。
  • 实例化线程池时需要指定线程池基本大小,最大大小,存活时间等,而使用工厂则是工厂已经帮我们设置好了这些参数。直接返回一个实例化的类。
ThreadPoolExecutor(int corePoolSize, 
                   int maximumPoolSize, 
                   long keepAliveTime, 
                   TimeUnit unit, 
                   BlockingQueue<Runnable> workQueue, 
                   ThreadFactory threadFactory, 
                   RejectedExecutionHandler handler){...}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您提供一个简单的Java Executor框架使用示例。 Java Executor框架Java SE 5引入的一个并发执行框架,它提供了一种方便的方式来管理并发任务的执行。在这个示例中,我们将使用Executor框架来执行一个简单的计算任务。 首先,我们需要定义一个实现Callable接口的任务类,该接口可以返回一个结果,并抛出一个可能发生的异常。这是一个简单的任务类示例: ``` import java.util.concurrent.Callable; public class MyTask implements Callable<Integer> { private int number; public MyTask(int number) { this.number = number; } @Override public Integer call() throws Exception { int result = 0; for (int i = 1; i <= number; i++) { result += i; } return result; } } ``` 这个任务类会计算从1到给定数字的和,并返回结果。接下来,我们需要创建一个ExecutorService,它是一个高级线程池实现,可以管理并发执行的任务。我们可以使用Executors类中的静态工厂方法来创建ExecutorService实例: ``` import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(new MyTask(10)); try { Integer result = future.get(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } executor.shutdown(); } } ``` 在这个示例中,我们创建了一个单线程的ExecutorService,然后使用submit()方法提交了一个MyTask实例。submit()方法返回一个Future对象,我们可以使用它来检查任务是否完成,并获取任务的结果。 在这个示例中,我们使用了future.get()方法来获取任务的结果。get()方法是一个阻塞方法,它会等待任务完成并返回结果,如果任务抛出了异常,它会将异常重新抛出。因此,在调用get()方法时,我们需要使用try-catch块来处理可能抛出的异常。 最后,我们需要调用ExecutorService的shutdown()方法来关闭线程池并释放资源。这个方法会等待所有任务完成并关闭线程池,所以我们应该在使用ExecutorService后立即调用它。 这就是一个简单的Java Executor框架使用示例。希望对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值