Java多线程——任务执行和取消

Executor框架

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

Executor基于生产者—消费者模式,用Runnable表示任务,提交任务操作相当于生产者,执行任务的线程相当于消费者

线程池

可通过Executors中的静态工厂方法创建线程池

  • newFixedThreadPool():固定长度线程池,每提交一个任务就创建一个线程,直到达到最大数量(如果线程出现Exception则会重新补充)
  • newCachedThreadPool():可缓存的线程池,数量不受限制,若当前数量超过处理需求,将回收空闲的线程
  • newSingleThreadExecutor():单线程线程池,确保任务串行执行(如果线程出现Exception则会重新补充)
  • newScheduledThreadPool():固定长度线程池,可以以延迟或定时的方式来执行任务

Executor生命周期

使用ExecutorService管理生命周期,其包括运行、关闭、已终止

  • ExecutorService在初始创建时处于运行状态
  • shutdown():不再接受新的任务,同时等待已经提交的任务执行完成
  • shutdownNow():尝试取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务
  • isTerminated():判断是否终止
  • awaitTermination():等待到达终止状态,通过调用shutdown()后再调用达到同步关闭效果
  • submit():将一个Runnalbe或Callable提交给Executor,并返回一个Future用于获取任务的执行结果或取消任务
  • invokeAll():执行一组Callable并返回一组Future,超过特定时间未完成的任务将会被取消
public interface ExecutorService extends Executor {

    void shutdown();

    List shutdownNow();

    boolean isShutdown();

    boolean isTerminated();

    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;
	
	 Future submit(Callable task);

	 Future submit(Runnable task, T result);

	Future submit(Runnable task);

	 List> invokeAll(Collection> tasks)
        throws InterruptedException;

	 List> invokeAll(Collection> tasks,
                                  long timeout, TimeUnit unit)
        throws InterruptedException;

	 T invokeAny(Collection> tasks)
        throws InterruptedException, ExecutionException;

	 T invokeAny(Collection> tasks,
                    long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
	......
}

延迟任务与周期任务

Timer负责管理延迟任务和周期任务,但

  • Timer在执行所有定时任务时只会创建一个线程,若某个TimerTask执行时间过长,将会影响其他TimerTask的精确性
  • 若TimerTask抛出异常将终止定时线程,Timer也不会恢复线程,导致剩下的任务未完成,称为线程泄漏

故应该使用ScheduledThreadPoolExecutor代替Timer

携带结果的任务Callable与Future

相比于Runnable,Callable可以返回一个值或抛出一个异常

public interface Callable {
    V call() throws Exception;
}

Future表示一个任务的生命周期,并提供方法判断任务是否完成、获取任务结果和取消任务等

public interface Future {

    boolean cancel(boolean mayInterruptIfRunning);

    boolean isCancelled();

    boolean isDone();

    V get() throws InterruptedException, ExecutionException;

    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

CompletionService

CompletionService将Executor和BlockingQueue的功能结合,将Callable任务提交给它,然后用take()获取一个future的结果

任务取消

通过设置取消标志,来取消任务执行

class MyTask implements Runnable {

    private volatile boolean cancelled;

    @Override
    public void run() {
        while (!cancelled) {
            //......
        }
    }

    public void cancel() {
        cancelled = true;
    }
}

中断

但对于如下生产者-消费者模式,生产者添加队列的速度大于消费者,会导致put()阻塞,此时再调用cancel()也无法退出,因为消费者已停止(队列将一直阻塞)

class MyTask implements Runnable {

    private volatile boolean cancelled;
    private final BlockingQueue mQueue;

    MyTask(BlockingQueue queue) {
        this.mQueue = queue;
    }

    @Override
    public void run() {
        int i = 0;
        while (!cancelled) {
            try {
                mQueue.put(i);
            } catch (InterruptedException e) {

            }
            //......
        }
    }

    public void consume() {
        while (!cancelled){
            try {
                Thread.sleep(1000);
                mQueue.take();
            } catch (InterruptedException e) {
                
            }
        }
    }

    public void cancel() {
        cancelled = true;
    }
}

在Thread中

  • interrupt():中断目标线程
  • isInterrupted():返回目标线程中断状态
  • interrupted():清除当前线程中断状态并返回它之前的值

修改为Thread,while通过判断中断或put阻塞时抛出中断异常来退出线程

class MyTask extends Thread {

    private final BlockingQueue mQueue;

    MyTask(BlockingQueue queue) {
        this.mQueue = queue;
    }

    @Override
    public void run() {
        int i = 0;
        while (!Thread.currentThread().isInterrupted()) {
            try {
                mQueue.put(i);
            } catch (InterruptedException e) {

            }
            //......
        }
    }

    public void consume() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                Thread.sleep(1000);
                mQueue.take();
            } catch (InterruptedException e) {
                
            }
        }
    }

    public void cancel() {
        interrupt();
    }
}

响应中断

当调用可中断的阻塞函数时,如Thread.sleep或BlockingQueue
.put,有两种策略处理InterruptedException

  • 传递异常
  • 恢复中断状态

关闭ExecutorService

class Task {
    public void close() {
        ExecutorService mExecutorService = Executors.newCachedThreadPool();
        mExecutorService.execute(new Runnable() {
            @Override
            public void run() {

            }
        });

        mExecutorService.shutdown();
        try {
            mExecutorService.awaitTermination(1000, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

}

守护线程

普通线程和守护线程的差异在于线程退出时发生的操作,当JVM中的普通线程都退出后,JVM会正常退出,如果仍存在守护线程则将被抛弃(不会执行finally)

你可能感兴趣的:(#,java多线程,java,windows,开发语言)