Java基础:线程池

  • 什么是线程池?
  • :一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行的任务的队列。
  • 线程池的作用
    • 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
    • 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
  • Java中的线程池种类
  • :Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
    • newSingleThreadExecutor
      • 创建方式
      • ExecutorService pool = Executors.newSingleThreadExecutor();
        
      • 一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
      • 使用方式
      • import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        public class ThreadPool {
            public static void main(String[] args) {
                ExecutorService pool = Executors.newSingleThreadExecutor();
                for (int i = 0; i < 10; i++) {
                    pool.execute(() -> {
                        System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
                    });
                }
            }
        }
        
      • 输出结果如下
      • 从输出的结果我们可以看出,一直只有一个线程在运行。
    • newFixedThreadPool
      • 创建方式
      • ExecutorService pool = Executors.newFixedThreadPool(10);
      • 创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
      • 使用方式
      • import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        public class ThreadPool {
            public static void main(String[] args) {
                ExecutorService pool = Executors.newFixedThreadPool(10);
                for (int i = 0; i < 10; i++) {
                    pool.execute(() -> {
                        System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
                    });
                }
            }
        }
        
      • 输出结果如下
    • newCachedThreadPool
      • 创建方式
      • ExecutorService pool = Executors.newCachedThreadPool();
      • 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又添加新线程来处理任务。
      • 使用方式
      • import java.util.concurrent.ExecutorService;
        import java.util.concurrent.Executors;
        public class ThreadPool {
        	public static void main(String[] args) {
        		ExecutorService pool = Executors.newCachedThreadPool();
                for (int i = 0; i < 10; i++) {
                    pool.execute(() -> {
                        System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
                    });
                }
        	}
        }
        
      • 使用方式
    • ​​​​​​​newScheduledThreadPool
      • ​​​​​​​​​​​​​​创建方式:​​​​​​​
      • ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
      • 此线程池支持定时以及周期性执行任务的需求。
      • 使用方式:​​​​​​​
      • import java.util.concurrent.Executors;
        import java.util.concurrent.ScheduledExecutorService;
        import java.util.concurrent.TimeUnit;
        public class ThreadPool {
            public static void main(String[] args) {
                ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
                for (int i = 0; i < 10; i++) {
                    pool.schedule(() -> {
                        System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
                    }, 10, TimeUnit.SECONDS);
                }
            }
        }
        
      • 上面演示的是延迟10秒执行任务,如果想要执行周期性的任务可以用下面的方式,每秒执行一次。
      • //pool.scheduleWithFixedDelay也可以
        pool.scheduleAtFixedRate(() -> {
                        System.out.println(Thread.currentThread().getName() + "\t开始发车啦....");
        }, 1, 1, TimeUnit.SECONDS);
        
    • ​​​​​​​newWorkStealingPool
      • ​​​​​​​newWorkStealingPool是jdk1.8才有的,会根据所需的并行层次来动态创建和关闭线程,通过使用多个队列减少竞争,底层用的ForkJoinPool来实现的。ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可
  • ​​​​​​​​​​​​​​​​​​​​​Java线程池中submit() 和 execute()方法有什么区别?
    :两个方法都可以向线程池提交任务
    • execute() 方法的返回类型是void,它定义在Executor接口中。 
    • submit() 方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,适用于需要处理返回着或者异常的业务场景​​​​​​​。其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。​​​​​​​
      • ​​​​​​​通过executor.submit提交一个线程,返回一个Future,然后通过这个Future的get方法取得返回值。
      • 在调用submit提交任务之后,主线程本来是继续运行了。但是运行到future.get()的时候就阻塞住了,一直等到任务执行完毕,拿到了返回的返回值,主线程才会继续运行。​​​​​​​​​​​​​​
      • 这里注意一下,他的阻塞性是因为调用get()方法时,任务还没有执行完,所以会一直等到任务完成,形成了阻塞。​​​​​​​
      • 任务是在调用submit方法时就开始执行了,如果在调用get()方法时,任务已经执行完毕,那么就不会造成阻塞。​​​​​​​
  • 五种线程池的使用场景
    • ​​​​​​​newSingleThreadExecutor:一个单线程的线程池,可以用于需要保证顺序执行的场景,并且只有一个线程在执行。
    • newFixedThreadPool:一个固定大小的线程池,可以用于已知并发压力的情况下,对线程数做限制。

    • newCachedThreadPool:一个可以无限扩大的线程池,比较适合处理执行时间比较小的任务。

    • newScheduledThreadPool:可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景。

    • newWorkStealingPool:一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行。

  • 线程池的关闭​​​​​​​​​​​​​​

  • :关闭线程池可以调用shutdownNow和shutdown两个方法来实现​​​​​​​

    • ​​​​​​​shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表​​​​​​​

    • public class ThreadPool {
          public static void main(String[] args) throws Exception {
              ExecutorService pool = Executors.newFixedThreadPool(1);
              for (int i = 0; i < 5; i++) {
                  System.err.println(i);
                  pool.execute(() -> {
                      try {
                          Thread.sleep(30000);
                          System.out.println("--");
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  });
              }
              Thread.sleep(1000);
              List<Runnable> runs = pool.shutdownNow();
          }
      }
      
    • 上面的代码模拟了立即取消的场景,往线程池里添加5个线程任务,然后sleep一段时间,线程池只有一个线程,如果此时调用shutdownNow后应该需要中断一个正在执行的任务和返回4个还未执行的任务,控制台输出下面的内容:

    • shutdown:当我们调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止已经提交或者正在执行中的任务。​​​​​​

    • public class ThreadPool {
          public static void main(String[] args) throws Exception {
              ExecutorService pool = Executors.newFixedThreadPool(1);
              for (int i = 0; i < 5; i++) {
                  System.err.println(i);
                  pool.execute(() -> {
                      try {
                          Thread.sleep(30000);
                          System.out.println("--");
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  });
              }
              Thread.sleep(1000);
              pool.shutdown();
              pool.execute(() -> {
                  try {
                      Thread.sleep(30000);
                      System.out.println("--");
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              });
          }
      }
      
    • 上面的代码模拟了正在运行的状态,然后调用shutdown,接着再往里面添加任务,肯定是拒绝添加的,请看输出结果:

    • 0
      1
      2
      3
      4
      Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task exercise.ThreadPool$$Lambda$2/455659002@eed1f14 rejected from java.util.concurrent.ThreadPoolExecutor@7229724f[Shutting down, pool size = 1, active threads = 1, queued tasks = 4, completed tasks = 0]
      	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
      	at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
      	at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
      	at exercise.ThreadPool.main(ThreadPool.java:26)
      --
    • ​​​​还有一些业务场景下需要知道线程池中的任务是否全部执行完成,当我们关闭线程池之后,可以用isTerminated来判断所有的线程是否执行完成,千万不要用isShutdown,isShutdown只是返回你是否调用过shutdown的结果。

    •  

      package exercise;
      
      import java.util.List;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      public class ThreadPool {
      	public static void main(String[] args) {
      		ExecutorService pool = Executors.newFixedThreadPool(1);
              for (int i = 0; i < 5; i++) {
                  System.err.println(i);
                  pool.execute(() -> {
                      try {
                          Thread.sleep(3000);
                          System.out.println("--");
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                  });
              }
              try {
              	Thread.sleep(1000);  
      		} catch (Exception e2) {
      			// TODO: handle exception
      		}
              pool.shutdown();
              while(true){  
                  if(pool.isTerminated()){  
                      System.out.println("所有的子线程都结束了!");  
                      break;  
                  }  
                  try {
                  	Thread.sleep(1000);  
      			} catch (Exception e2) {
      				// TODO: handle exception
      			}
              }  
      	}
      }
      
      
    • 显示结果

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值