java并发之线程池api介绍

ScheduledThreadPoolExecutor

      ScheduledThreadPoolExecutor类是ScheduleExecutorService接口的实现类。
      这个接口是用来开启延时任务的线程池。
      他会将这些任务都放入一个队列,先进先出。

scheduleAtFixedRate
      只有前一个任务执行完毕之后,后面的任务才能接着去执行;如果前一个任务执行的时间超过了周期时间,等前一个任务执行完毕之后,立即执行后面的任务。
      如果前一个任务执行的时间少于周期时间,则需要再等待周期时间减任务执行时间才会执行后一个任务。

案例:


public class TestMain {
    static int a=0;
    
    public static void main(String[] args) {
    
	//延迟周期任务线程池,corePoolSize表示线程池中的线程数量,这个数量包括空闲数量
	ScheduledThreadPoolExecutor executor=new ScheduledThreadPoolExecutor(3);
	/*
	    将任务防止到延迟线程池中,这里表示一开始延迟4秒钟,后面每10秒执行一次这个线程
	 */
	executor.scheduleAtFixedRate(new Runnable() {
	    @Override
	    public void run() {
	        System.out.println("测试:"+(TestMain.a++));
	        try {
                    Thread.sleep(4000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
	    }
	}, 4, 10, TimeUnit.MILLISECONDS);

  }
}

如果正常情况下,当前任务执行完之后需要再等待6秒之后才会执行下一个任务。
输出结果:
测试:0
测试:1
测试:2
测试:3
。。。。。。

如果是scheduleWithFixedDelay 方法的话,虽然结果是一样的。但是当前任务执行完之后需要再等待10秒之后才会执行下一个任务。

scheduleWithFixedDelayscheduleAtFixedRate的区别就是一个在运行任务的时候就计时,一个任务运行完之后才计时。

任务队列

BlockingQueue

主要是用来存错已经提交的任务,但是该任务没未能马上分配到线程去执行的任务,该api是一个接口。

常用的实现类有:
1、LinkedBlockingQueue
    该api理论上是无界限,除非系统资源耗尽,否则可以一直讲任务存入该队列中。
    采用先进先出策略。
2、ArrayBlockingQueue
    有界限的任务队列,必须传入一个代表该队列最大容量的参数。
    采用先进先出策略。
3、SynchronousQueue
直接提交的队列,提交的任务将会被直接分配线程执行。如果线程数达到最大数量,则会执行拒绝策略。
4、PriorityBlockingQueue
    可指定任务的优先顺序,优先级越高其任务越优先分配到线程去执行。




使用Executors提供的五大线程池创建线程池

newSingleThreadExecutor

创建一个只有一个线程的线程池,其任务队列使用的是LinkedBlockingQueue无界限队列。
因为这里的队列是采用的FIFO算法,而且线程池的线程数量又只有一个,因此这里的请求是有顺序的。

任务是按添加的顺序执行的,但是他不能保证每个线程执行的结果是有序的。

  //创建单个线程的线程池
      /*  ExecutorService executor= Executors.newSingleThreadExecutor();

        for(int a=0;a<10;a++){
            PrintTask task=new PrintTask(a);
            executor.execute(task);
        }
        executor.shutdown();
*/

newCachedThreadPool

一开始没有固定的线程数,当有任务直接创建一个线程,如果有空闲的线程直接使用,若没有则继续创建。

        //创建一个无界限的线程池
     /* ExecutorService executorService=Executors.newCachedThreadPool();
      for(int a=0;a<100;a++){
        PrintTask task=new PrintTask(a);
        executorService.execute(task);
      }
        executorService.shutdown();*/

newFixedThreadPool

创建一个固定数量的线程池,其任务队列使用的是LinkedBlockingQueue无界限队列。

        //固定数量的线程池
    /* ExecutorService executorService=Executors.newFixedThreadPool(10);
     for(int a=0;a<20;a++){
         PrintTask task=new PrintTask(a);
         //当任务超过线程池的线程池数量之后,会把任务放入线程池的任务队列种。
         executorService.execute(task);
     }*/

newScheduledThreadPool

        ExecutorService executorService = Executors.newScheduledThreadPool(30);
        for (int a = 0; a < 10; a++) {
            PrintTask task = new PrintTask(a);
            executorService.execute(task);
        }
        executorService.shutdown();

PrintTask类:

public class PrintTask implements Runnable {
    private int a;

    public PrintTask(int a) {
        this.a = a;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "----" + a);
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程结束----" + a);
    }
}

newWorkStealingPool

    该线程池和其他四个都不同,他创建线程池传递的参数是parallelism ,表示并发数量,创建的任务队列的个数是 parallelism+1 个,线程也是paralleism+1个,当各自的线程维护的队列的任务完成之后,就会从其他的线程队里偷取任务来执行。
 这也是newWorkStealingPool和以上四种线程数很大不同的区别,而且其他四种线程只维护一个任务队列。



ExecutorService

ExecutorService注重于线程池的管理。

submit():
	提交一个可运行的任务到线程池中,并返回一个该任务的Future(任务执行结果的一个包装对象),Future提供的get方法,将在成功完成后返回给定的结果,get()方法可以配置等待时间。
	Future对象也可以取消这个任务的执行。
execute():
	将该任务添加到线程池中,在某个时间执行。和submit不同的是,execute方法没有返回值。


shutdown():
	方法允许先前提交的任务在线程池终止之前执行,但不会接受新的任务了,若任务全部执行完毕,线程池终止。
	这个方法会先让线程池的状态预设置成停止,但只有在调用该方法之前的任务执行完之后才会停止掉线程池。
	
shutdownNow():
	阻止任务队列中任务的启动并且尝试停止正在执行的任务,一旦终止,线程池中将没有任务执行,并且不能提交新的任务。
	说笼统些,就是立马停止线程池,对于正在执行的任务会尝试去中断,它是通过interrupt()方法去中断任务的,对于一些没有sleep,wait,Condition的任务来说是没有办法中断的,只能等待执行完毕之后才停止线程池。

isisTerminated():
	只有当线程池关闭了,该方法才会返回true

例如:

public class TestExecutors {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = new ThreadPoolExecutor(1, 3, 5,
                TimeUnit.SECONDS, new LinkedBlockingDeque<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        System.out.println("inited");

        for (int i = 0; i < 20; i++) {
            int finalI = i;
            executorService.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(10);
                    System.out.println(finalI);
                    System.out.println(Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

        }

        System.out.println(executorService.isTerminated());
        executorService.shutdown();
        System.out.println(executorService.isTerminated());
        System.out.println(1111111);
    }
}

结果:
java并发之线程池api介绍_第1张图片

从结果上看到,调用了shutdown() 方法,并不是真正的将线程池关闭了。但如果换成shutdownNow()就会直接关闭线程池了。




CompletionService

该接口和ExecutorService十分类似,不过CompletionService更注重的是任务队列的执行情况,实际上CompletionServiceExecutorBlockQueue的结合。
事实上CompletionService就是用来管理线程管理线程池提交的那些任务完成后的结果的一个api。

task():
	等待当前执行的任务的完成情况,返回Future对象。



任务的创建有两种创建方式:RunnaleCallable
1、Runnable:这种方式一般是用来执行无返回值的。

2、Callable:这种任务的创建方式可以返回执行结果。

可以看成一个线程就是一个任务,然后让Executors工厂提供一个线程池去管理这些任务。ExecutorService主要

Executors是操作线程池的工具类,Executor是所有线程池的终极父接口,ExecutorService比较注重任务的运行和管理。

ForkJoinTask:可以将一个任务分解成多个,然后执行完之后再将任务的结果合并。
ForkJoinTask它的实现类有RecursiveTask。

注意
线程池技术,内部使用了一个任务队列的方式。当任务超出了最大线程数量之后,则会将任务阻塞在任务队列中,然后等待现在正在执行任务的线程,执行完当前任务之后,才会去从任务队列中取任务执行,知道所有任务执行完毕。

你可能感兴趣的:(java)