[关闭]
@XQF 2017-02-17T15:05:32.000000Z 字数 4704 阅读 1772

Java并发之线程池

java


参考博文

1.简述

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

2.Executor执行Runnable任务

  1. class TestRunnable implements Runnable {
  2. public void run() {
  3. System.out.println(Thread.currentThread().getName() + "线程被调用了。");
  4. }
  5. }
  6. public class TestCachedThreadPool {
  7. public static void main(String[] args) {
  8. // 1、创建一个单线程池
  9. ExecutorService executorService = Executors.newSingleThreadExecutor();
  10. //2、往线程池中添加任务
  11. for (int i = 0; i < 5; i++) {
  12. executorService.execute(new TestRunnable());
  13. System.out.println("************* a" + i + " *************");
  14. }
  15. // 3、关闭线程池
  16. executorService.shutdown();
  17. }
  18. }

1.创建线程池

Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

  1. //创建固定数目线程的线程池
  2. public static ExecutorService newFixedThreadPool(int nThreads)
  3. //创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线 程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
  4. public static ExecutorService newCachedThreadPool()
  5. //创建一个单线程化的Executor。
  6. public static ExecutorService newSingleThreadExecutor()
  7. //创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
  8. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

2.添加Runnable任务

使用executorService的execute()方法将任务添加到线程,线程就会自动的执行Runnable的run方法。

3.关闭线程池

ExecutorService的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了shutdown()方法时,便进入关闭状态,此时意味着ExecutorService不再接受新的任务,但它还在执行已经提交了的任务,当所有已经提交了的任务执行完后,便到达终止状态。如果不调用shutdown()方法,ExecutorService会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。

3.Executor执行Callable任务

Callable任务与Runnable任务不同的就是Callable任务有返回值,任务执行完成之后,它可以得到任务的返回值。返回Future对象,我们可以根据这个对象的isDone函数来判断是否这个对象已经得到了返回值,如果为ture,就表示已经得到了返回值,这样我使用Future的get方法就可以得到任务的返回值。

  1. class TaskWithResult implements Callable<String> {
  2. private int id;
  3. public TaskWithResult(int id) {
  4. this.id = id;
  5. }
  6. /**
  7. * 任务的具体过程,一旦任务传给ExecutorService的submit方法,
  8. * 则该方法自动在一个线程上执行
  9. */
  10. public String call() throws Exception {
  11. System.out.println("call()方法被自动调用!!! " + Thread.currentThread().getName());
  12. //该返回结果将被Future的get方法得到
  13. return "call()方法被自动调用,任务返回的结果是:" + id + " " + Thread.currentThread().getName();
  14. }
  15. }
  16. public class CallableDemo {
  17. public static void main(String[] args) {
  18. // 1、创建一个线程池
  19. ExecutorService executorService = Executors.newCachedThreadPool();
  20. // 创建一个List来存放任务的返回future
  21. List<Future<String>> resultList = new ArrayList<>();
  22. //创建10个任务并执行
  23. for (int i = 0; i < 10; i++) {
  24. //2、向线程中添加Callable任务,Callable任务的返回值存放在future中
  25. Future<String> future = executorService.submit(new TaskWithResult(i));
  26. //将任务执行结果存储到List中
  27. resultList.add(future);
  28. }
  29. //遍历任务的结果
  30. for (Future<String> fs : resultList) {
  31. try {
  32. //Future返回如果没有完成,则一直循环等待,直到Future返回完成
  33. while (!fs.isDone()) ;
  34. //打印各个任务返回的结果
  35. System.out.println(fs.get());
  36. } catch (InterruptedException e) {
  37. e.printStackTrace();
  38. } catch (ExecutionException e) {
  39. e.printStackTrace();
  40. } finally {
  41. //3、关闭线程池
  42. executorService.shutdown();
  43. }
  44. }
  45. }
  46. }

1.创建线程池

跟上面一样

2.添加Callable任务到线程中

这个跟上面的不同就是它可以得到一个返回值future,我们可以通过future里面的isDone()函数来判断是否结果以及被返回,如果Callable任务的结果还没有被返回,isDone函数的结果为false,否则为true,这样就可以通过future的get方法得到返回值,其实我们也可以不用判断,直接调用get函数,如果任务没有返回结果,他会被阻塞,直到结果返回。使用executorService的submit函数将任务添加到线程,线程会自动调用它的call方法。

3.关闭线程池

跟上面一样

4.Executor执行FutureTask任务

FutureTask有一个cancel方法,我们可以调用这个方法来取消任务,如果任务被取消,那么任务就不会被运行。另外它也具备第二种方法的优点,它可以得到返回结果,不过不是通过返回值的形式。

  1. class DemoTask implements Callable<String> {
  2. public String call() {
  3. try {
  4. Thread.sleep(2000);
  5. } catch (InterruptedException e) {
  6. System.out.println(e);
  7. }
  8. return "Task Done";
  9. }
  10. }
  11. public class FutureTaskDemo {
  12. public static void main(String... args) throws InterruptedException, ExecutionException {
  13. // 1、创建一个线程池
  14. ExecutorService exService = Executors.newSingleThreadExecutor();
  15. // 2、创建一个FutureTask任务
  16. FutureTask<String> futureTask = new FutureTask<>(new DemoTask());
  17. // 3、将任务放入线程池
  18. exService.execute(futureTask);
  19. //检测任务是否执行完成
  20. System.out.println(futureTask.isDone());
  21. //检测任务是否被取消
  22. System.out.println(futureTask.isCancelled());
  23. //获取返回结果
  24. System.out.println(futureTask.get());
  25. }

1.创建线程池

跟上面的一样

2.添加FutrueTask任务

execute()方法或者submit()方法都行

3.关闭线程池

5.自定义线程池

用ThreadPoolExecutor类创建线程池,我们直接来看代码。

  1. class MyThread implements Runnable{
  2. @Override
  3. public void run(){
  4. System.out.println(Thread.currentThread().getName() + "正在执行。。。");
  5. try{
  6. Thread.sleep(100);
  7. }catch(InterruptedException e){
  8. e.printStackTrace();
  9. }
  10. }
  11. }
  12. public class ThreadPoolTest{
  13. public static void main(String[] args){
  14. //1、创建等待队列
  15. BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20);
  16. //2、创建线程池,池中保存的线程数为3,允许的最大线程数为5
  17. ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,50, TimeUnit.MILLISECONDS,bqueue);
  18. Runnable t1 = new MyThread();
  19. Runnable t2 = new MyThread();
  20. Runnable t3 = new MyThread();
  21. Runnable t4 = new MyThread();
  22. Runnable t5 = new MyThread();
  23. Runnable t6 = new MyThread();
  24. Runnable t7 = new MyThread();
  25. //3、向线程池添加任务
  26. pool.execute(t1);
  27. pool.execute(t2);
  28. pool.execute(t3);
  29. pool.execute(t4);
  30. pool.execute(t5);
  31. pool.execute(t6);
  32. pool.execute(t7);
  33. //4、关闭线程池
  34. pool.shutdown();
  35. }
  36. }

注意等待队列

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注