线程池就是一个可以复用线程的技术,可以提高线程的利用率。
线程池的好处:
1.避免因为线程的创建和销毁带来的性能开销
2.有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞
3.能够对线程进行简单的管理,并提供以下特定的操作:定时、定期、单线程、并发数控制等功能
线程池可能带来的风险:
1.死锁
2.资源不足(一般是由于线程池设置过大,需要合理设置线程池大小)
3.并发错误,线程池和其他排队机制依靠使用wait()和notify()方法,这两个方法都难于使用。如果编码不正确,可能丢失通知,导致线程保持空闲状态,尽管队列中有工作要处理。
4.线程泄露,任务抛出一个RuntimeException或一个Error时。如果池类没有捕捉到它们,那么线程只会退出而线程池的大小将会永久减少一个。当这种情况发生的次数足够多时,线程池最终为空,系统将停止(因为没有可用的线程来处理任务)。
5.请求过载,线程池任务队列中,待执行的任务堆积过多,消耗太多的系统资源并引起资源缺乏。
Executor框架:
Executor:
在我们使用线程池来执行一些任务时,线程池提供了两个常用的方法,即execute()和submit()。它们都可以用来提交任务,但在使用时需要注意它们之间的区别。
execute是Executor接口中声明的抽象方法,在ThreadPoolExecutor类中有具体实现。execute方法用于提交不需要返回值的任务,它将任务提交到线程池中执行,并立即返回,不会等待任务执行完成。如果任务执行过程中出现异常,线程池将捕获异常并将其记录下来。其他线程继续执行新任务。
submit是ExecutorService接口的方法,在AbstractExecutorService抽象类中已经有具体实现。submit方法用于提交需要返回值的任务,它会返回一个Future对象,可以用来获取任务执行结果。submit方法会将任务提交到线程池中执行,并立即返回,但不会阻塞当前线程,它会立即返回一个Future对象,通过该对象可以获取任务执行结果。如果任务执行过程中出现异常,submit无提示,其他线程继续执行。想要捕获异常信息需要通过Future对象的get()方法将会抛出异常。
需要注意的是,submit方法可以用于提交Callable任务和Runnable任务,而execute方法只能用于提交Runnable任务。因此,如果需要执行一个Callable任务并获取其结果,必须使用submit方法。
阻塞队列:
阻塞队列是一种特殊的队列,支持在队列为空时阻塞等待,或队列已满时阻塞等待。
java.util.concurrent包提供了多种类型的阻塞队列,例如ArrayBlockingQueue、LinkedBlockingQuere、SynchronousQueue等。
阻塞队列特性:
1.支持阻塞等待:当队列为空或满时,从队列中获取或添加元素的操作会被阻塞,直到队列中有新的元素或空间可用。
2.线程安全:多个线程可以同时访问同一个阻塞队列。
3.FIFO顺序:先进先出的顺序
4.支持多种操作:包括添加、移除、检查等