Java集合框架面试题

编程之家收集整理的这篇文章主要介绍了Java集合框架面试题编程之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

Arraylist 与 LinkedList 异同

1. 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;

2. 底层数据结构: Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;

3. 插入和删除是否受元素位置的影响:ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index,E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。

4. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

5. 内存空间占用: ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。

补充:数据结构基础之双向链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表,如下图所示,同时下图也是LinkedList 底层使用的是双向循环链表数据结构。

 

 

 

ArrayList 与 Vector 区别

Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。

Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。

HashMap的底层实现

JDK1.8之前

JDK1.8 之前 HashMap 由 数组+链表 组成的(“链表散列” 即数组和链表的结合体),数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(HashMap 采用 “拉链法也就是链地址法” 解决冲突),如果定位到的数组位置不含链表(当前 entry 的 next 指向 null ),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为 O(1),因为最新的 Entry 会插入链表头部,急需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过 key 对象的 equals 方法逐一比对查找.

所谓 “拉链法” 就是将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。

 

jdk1.8之前的内部结构

 

 

JDK1.8之后

相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(认为8)时,将链表转化为红黑树,以减少搜索时间。

 

JDK1.8之后的HashMap底层数据结构

 

 

TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。

推荐阅读:

《Java 8系列之重新认识HashMap》 :@L_419_0@

HashMap 和 Hashtable 的区别

    线程是否安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);

    效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;

    对Null key 和Null value的支持 HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。

    初始容量大小和每次扩充容量大小的不同 : ①创建时如果不指定容量初始值,Hashtable 认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。

    底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。

HashMap 的长度为什么是2的幂次方

为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。

这个算法应该如何设计呢?

我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。

HashSet 和 HashMap 区别

HashSet 和 HashMap 区别

ConcurrentHashMap 和 Hashtable 的区别

ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。

底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;

实现线程安全的方式(重要):在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(认分配16个Segment,比Hashtable效率提高16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。

两者的对比图:

图片来源:www.cnblogs.com/chengxiao/p…

HashTable:

 

JDK1.7的ConcurrentHashMap:

JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点):

ConcurrentHashMap线程安全的具体实现方式/底层具体实现

JDK1.7(上面有示意图)

首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。

ConcurrentHashMap 是由 Segment 数组结构和 HahEntry 数组结构组成

Segment 实现了 reentrantlock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。

static class Segmentextends reentrantlock implements Serializable {

一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。

JDK1.8 (上面有示意图)

ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。

synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。

集合框架底层数据结构总结

Collection

1. List

Arraylist: Object数组

Vector: Object数组

LinkedList: 双向循环链表

2. Set

HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素

LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。

TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)

Map

HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(认为8)时,将链表转化为红黑树,以减少搜索时间

LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。详细可以查看:《LinkedHashMap 源码详细分析(JDK1.8)》

HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的

TreeMap: 红黑树(自平衡的排序二叉树)

总结

以上是编程之家为你收集整理的Java集合框架面试题全部内容,希望文章能够帮你解决Java集合框架面试题所遇到的程序开发问题。

如果觉得编程之家网站内容还不错,欢迎将编程之家网站推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
喜欢与人分享编程技术与工作经验,欢迎加入编程之家官方交流群!

相关文章

猜你在找的Java面试题相关文章

聊聊SpringBoot和传统的SSM的区别? SpringBoot是Spring的扩展,在Spring的基础上,简化了传统的SSM开发繁琐的配置; 在部署上,SpringBoot内置了Tomcat,
在面试当中,有时候会问到你在项目中用过多线程么? 对于普通的应届生或者工作时间不长的初级开发 ???—— crud仔流下了没有技术的眼泪。 博主这里整理了项目中用到了多线程的一个简单的实例,希望能对你
前言 本文分为20多个问题,通过问题的方式,来逐渐理解jvm,由浅及深。希望帮助到大家。 1. Java类实例化时,JVM执行顺序? 正确的顺序如下: 1父类静态代码块 2父类静态变量 3子
1. Java线程的创建方式 (1)继承thread类 thread类本质是实现了runnable接口的一个实例,代表线程的一个实例。启动线程的方式start方法。start是一个本地方法,
1. List List 是有序的 Collection。Java List 一共三个实现类: 分别是 ArrayList、Vector 和 LinkedList ArrayList Arr
一、Spring面试题1、Spring 在ssm中起什么作用?Spring:轻量级框架作用:Bean工厂,用来管理Bean的生命周期和框架集成。两大核心:①. IOC/DI(控制反转/依赖注入) :把dao依赖注入到service层,service层反转给action层,Spring顶层容器为BeanFactory。②. AOP:面向切面编程2、Spring的事务?编程式事务管理:编程方式管理事务,极大灵活性,难维护。声明式事务管理:可以将业务代码和事务管理分离,用注解和xml配置来管理事务。3、IOC 在项目中的作用?作用:Ioc解决对象之间的依赖问题,把所有Bean的依赖关系通过配置文件或注解关联起来,降低了耦合度。4、Spring的配置文件中的内容?开启事务注解驱动事务管理器开启注解功能,并配置扫描包配置数据库配置SQL会话工厂,别名,映射文件不用编写Dao层的实现类5、Spring下的注解?注册:@Controller @Service @Component注入:@Autowired @Resource请求地址:@RequestMapping返回具体数据类型而非跳转:@ResponseBody6、Spring DI 的三种方式?构造器注入:通过构造方法初始化<constructor-arg index="0" type="java.lang.String" value="宝马"></constructor-arg>setter方法注入:通过setter方法初始化<property name="id" value="1111"></property>接口注入7、Spring主要使用了什么模式?工厂模式:每个Bean的创建通过方法单例模式:默认的每个Bean的作用域都是单例代理模式:关于Aop的实现通过代理模式8、IOC,AOP的实现原理?IOC:通过反射机制生成对象注入AOP:动态代理二、SpringMvc面试题1、SpringMvc 的控制器是不是单例模式,如果是,有什么问题,怎么解决?问题:单例模式,在多线程访问时有线程安全问题解决方法:不要用同步,在控制器里面不能写字段2、SpringMvc 中控制器的注解?@Controller:该注解表明该类扮演控制器的角色3、@RequestMapping 注解用在类上的作用?作用:用来映射一个URL到一个类或者一个特定的处理方法上4、前台多个参数,这些参数都是一个对象,快速得到对象?方法:直接在方法中声明这个对象,SpringMvc就自动把属性赋值到这个对象里面5、SpringMvc中函数的返回值?String,ModelAndView,List,Set 等一般String,Ajax请求,返回一个List集合6、SpringMvc中的转发和重定向?转发: return:“hello”重定向 :return:“redirect:hello.jsp”7、SpringMvc和Ajax之间的相互调用?通过JackSon框架把java里面对象直接转换成js可识别的json对象,具体步骤如下:加入JackSon.jar在配置文件中配置json的映射在接受Ajax方法里面直接返回Object,list等,方法前面需要加上注解@ResponseBody8、SpringMvc的工作流程图? 9、Struts2 和 SpringMvc的区别?入口不同:Struts2:filter过滤器SpringMvc:一个Servlet即前端控制器开发方式不同:Struts2:基于类开发,传递参数通过类的属性,只能设置为多例SpringMvc:基于方法开发(一个url对应一个方法),请求参数传递到方法形参,可以为单例也可以为多例(建议单例)请求方式不同:Struts2:值栈村塾请求和响应的数据,通过OGNL存取数据SpringMvc:通过参数解析器将request请求内容解析,给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面,jsp视图解析器默认使用的是jstl。三、Mybatis面试题1、Ibatis和Mybatis?Ibatis:2010年,apache的Ibatis框架停止更新,并移交给了google团队,同时更名为MyBatis。从2010年后Ibatis在没更新过,彻底变成了一个孤儿框架。一个没人维护的框架注定被mybatis拍在沙滩上。Mybatis:Ibatis的升级版本。2、什么是Mybatis的接口绑定,有什么好处?Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便。3、什么情况用注解,什么情况用xml绑定?注解使用情况:Sql语句简单时xml绑定使用情况:xml绑定 (@RequestMap用来绑定xml文件)4、Mybatis在核心处理类叫什么?SqlSession5、查询表名和返回实体Bean对象不一致,如何处理?映射键值对即可<result column="title" property="title" javaType="java.lang.String"/>column:数据库中表的列名property:实体Bean中的属性名6、Mybatis的好处?把Sql语句从Java中独立出来。封装了底层的JDBC,API的调用,并且能够将结果集自动转换成JavaBean对象,简化了Java数据库编程的重复工作。自己编写Sql语句,更加的灵活。入参无需用对象封装(或者map封装),使用@Param注解7、Mybatis配置一对多?<collection property="topicComment" column="id" ofType="com.tmf.bbs.pojo.Comment" select="selectComment" />property:属性名column:共同列ofType:集合中元素的类型select:要连接的查询8、Mybatis配置一对一?<association property="topicType" select="selectType" column="topics_type_id" javaType="com.tmf.bbs.pojo.Type"/>property:属性名select:要连接的查询column:共同列javaType:集合中元素的类型9 、${} 和 #{}的区别?${}:简单字符串替换,把${}直接替换成变量的值,不做任何转换,这种是取值以后再去编译SQL语句。#{}:预编译处理,sql中的#{}替换成?,补全预编译语句,有效的防止Sql语句注入,这种取值是编译好SQL语句再取值。总结:一般用#{}来进行列的代替10、获取上一次自动生成的主键值?select last _insert_id()11、Mybatis如何分页,分页原理?RowBounds对象分页在Sql内直接书写,带有物理分页12、Mybatis工作原理? 原理:通过SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory。SqlSessionFactory开启一个SqlSession,通过SqlSession实例获得Mapper对象并且运行Mapper映射的Sql语句。完成数据库的CRUD操作和事务提交,关闭SqlSession。四、结语前面如有不正确的地方还希望大家多多指教,希望和志同道合的朋友一起学习,一起进步,先更新到这里,下次继续补充。
面向对象编程的基本理念与核心设计思想解释下多态性(polymorphism),封装性(encapsulation),内聚(cohesion)以及耦合(coupling)。继承(Inheritance)与聚合(Aggregation)的区别在哪里。你是如何理解干净的代码(Clean Code)与技术负载(Technical Debt)的。描述下常用的重构技巧。阐述下 SOLID 原则。其他的譬如 KISS,DRY,YAGNI 等原则又是什么含义。什么是设计模式(Design Patterns)?你知道哪些设计模式?你有了解过存在哪些反模式(Anti-Patterns)吗?你会如何设计登陆舰/数学表达式计算程序/一条龙?你知道哪些基本的排序算法,它们的计算复杂度如何?在给定数据的情况下你会倾向于使用哪种算法呢?尝试编写如下代码:计算指定数字的阶乘开发 Fizz Buzz 小游戏倒转句子中的单词回文字符串检测枚举给定字符串的所有排列组合Java 核心概念equals 与 hashCode 的异同点在哪里?Java 的集合中又是如何使用它们的。描述下 Java 中集合(Collections),接口(Interfaces),实现(Implementations)的概念。LinkedList 与 ArrayList 的区别是什么?基础类型(Primitives)与封装类型(Wrappers)的区别在哪里?final 与 static 关键字可以用于哪里?它们的作用是什么?阐述下 Java 中的访问描述符(Access Modifiers)。描述下 String,StringBuilder 以及 StringBuffer 区别。接口(Interface)与抽象类(Abstract Class)的区别在哪里。覆盖(Overriding)与重载(OverLoading)的区别在哪里。异常分为哪几种类型?以及所谓的handle or declare原则应该如何理解?简述垃圾回收器的工作原理。你是如何处理内存泄露或者栈溢出问题的?如何构建不可变的类结构?关键点在哪里?什么是 JIT 编译?Java 8 / Java 7 为我们提供了什么新功能?即将到来的 Java 9 又带来了怎样的新功能?Hibernate / 数据库请解释下 ORM。简述下 Hibernate 的优劣特性。Hibernate 与 JPA 区别在哪?Hibernate 最新版提供了哪些特性?什么是懒加载(Lazy Loading)?什么是 N+1 难题?介绍一些熟悉的 Hibernate 注释。简介下 Hibernate Session 与 SessionFactory。Entity Beans 的状态有哪些。Hibernate 中的缓存分为几层。Hibernate 中事务的支持分为几级?什么是乐观锁(Optimistic Locking)?简述下 ACID 原则。简述下数据库正则化(Normalizations)。请介绍下你日常工作中优化慢查询(Slow Query)的策略。Spring新版的 Spring 中有哪些新特性?介绍下 Spring 的优势与缺陷。什么是控制反转(Inversion of Control)与依赖注入(Dependency Injection)?你用过哪些 Spring 的模块?Spring 中是如何使用依赖注入的?Spring 中提供了几种自动注入的机制?介绍下 Spring MVC。Spring 中 Scopes 有哪些?Spring 中 Bean 的生命周期包含哪些步骤?Spring Bean 与 EJB Bean 的区别在哪里?其他主题介绍下切面编程(Aspect Oriented Programming)。概述下 GET 与 POST 的区别。Web Server、Web Container 与 Application Server 的区别是什么?简要介绍下从浏览器输入 URL 开始到获取到请求界面之后 Java Web 应用中发生了什么。什么是 N 层架构?微服务(MicroServices)与巨石型应用(Monolithic Applications)之间的区别在哪里?你知道哪些商业级设计模式?你是如何测试一个应用的?知道哪些测试框架?你是如何测试单个方法的?在你的职业生涯中,算得上最困难的技术挑战是什么?什么是领域驱动开发(Domain Driven Development)?介绍下一些你最爱的 IDE 的常用插件。除了 IDE 之外,你的日常工作中还会用到哪些工具?你使用什么版本管理工具?分支(Branch)与标签(Tag)之间的区别在哪里?你常用的持续集成(Continuous Integration)、静态代码分析(Static Code Analysis)工具有哪些?
1、什么是线程池线程池的基本思想是一种对象池,在程序启动时就开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。2、使用线程池的好处减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。运用线程池能有效的控制线程最大并发数,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。对线程进行一些简单的管理,比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现3、线程池的主要组件  一个线程池包括以下四个基本组成部分:线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;工作线程(WorkThread):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。4、ThreadPoolExecutor类讲到线程池,要重点介绍java.uitl.concurrent.ThreadPoolExecutor类,ThreadPoolExecutor线程池中最核心的一个类,ThreadPoolExecutor在JDK中线程池常用类UML类关系图如下: 我们可以通过ThreadPoolExecutor来创建一个线程池 new ThreadPoolExecutor(corePoolSize, maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue, threadFactory,handler);1. 创建一个线程池需要输入几个参数corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,Debug和定位问题时非常又帮助。RejectedExecutionHandler(拒绝策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。n AbortPolicy:直接抛出异常。keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。2. 向线程池提交任务我们可以通过execute()或submit()两个方法向线程池提交任务,不过它们有所不同execute()方法没有返回值,所以无法判断任务知否被线程池执行成功threadsPool.execute(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub}});submit()方法返回一个future,那么我们可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值try {Object s = future.get();} catch (InterruptedException e) {// 处理中断异常} catch (ExecutionException e) {// 处理无法执行任务异常} finally {// 关闭线程池executor.shutdown();}3. 线程池的关闭我们可以通过shutdown()或shutdownNow()方法来关闭线程池,不过它们也有所不同shutdown的原理是只是将线程池的状态设置成SHUTDOWN状态,然后中断所有没有正在执行任务的线程。shutdownNow的原理是遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的任务可能永远无法终止。shutdownNow会首先将线程池的状态设置成STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表。4. ThreadPoolExecutor执行的策略  线程数量未达到corePoolSize,则新建一个线程(核心线程)执行任务线程数量达到了corePools,则将任务移入队列等待队列已满,新建线程(非核心线程)执行任务队列已满,总线程数又达到了maximumPoolSize,就会由(RejectedExecutionHandler)抛出异常新建线程 -> 达到核心数 -> 加入队列 -> 新建线程(非核心) -> 达到最大数 -> 触发拒绝策略5. 四种拒绝策略AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满,线程池默认策略DiscardPolicy:不执行新任务,也不抛出异常,基本上为静默模式。DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行CallerRunPolicy:拒绝新任务进入,如果该线程池还没有被关闭,那么这个新的任务在执行线程中被调用)5、Java通过Executors提供四种线程池CachedThreadPool():可缓存线程池。线程数无限制有空闲线程则复用空闲线程,若无空闲线程则新建线程 一定程序减少频繁创建/销毁线程,减少系统开销FixedThreadPool():定长线程池。可控制线程最大并发数(同时执行的线程数)超出的线程会在队列中等待ScheduledThreadPool():定时线程池。支持定时及周期性任务执行。SingleThreadExecutor():单线程化的线程池。有且仅有一个工作线程执行任务所有任务按照指定顺序执行,即遵循队列的入队出队规则1. newCachedThreadPoolnewCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程 public class ThreadPoolExecutorTest1 {public static void main(String[] args) {ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 1000; i++) {final int index = i;try {Thread.sleep(index * 1000);} catch (Exception e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName()+":"+index);}});}}} 2. newFixedThreadPoolnewFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,指定线程池中的线程数量和最大线程数量一样,也就线程数量固定不变示例代码如下public class ThreadPoolExecutorTest {public static void main(String[] args) {ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);// 每隔两秒打印3个数for (int i = 0; i < 10; i++) {final int index = i;fixedThreadPool.execute(new Runnable() {public void run() {try {System.out.println(Thread.currentThread().getName()+":"+index);//三个线程并发Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}}} 3. newscheduledThreadPoolnewscheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下.表示延迟1秒后每3秒执行一次public class ThreadPoolExecutorTest3 {public static void main(String[] args) {ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);scheduledThreadPool.scheduleAtFixedRate(new Runnable() {public void run() {System.out.println(Thread.currentThread().getName() + ": delay 1 seconds, and excute every 3 seconds");}}, 1, 3, TimeUnit.SECONDS);// 表示延迟1秒后每3秒执行一次}}  4. newSingleThreadExecutornewSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行public class ThreadPoolExecutorTest4 {public static void main(String[] args) {ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int index = i;singleThreadExecutor.execute(new Runnable() {public void run() {try {System.out.println(Thread.currentThread().getName() + ":" + index);Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});}}}结果依次输出,相当于顺序执行各个任务。使用JDK自带的监控工具来监控我们创建的线程数量,运行一个不终止的线程
微信公众号搜索 “ 程序精选 ” ,选择关注!
微信公众号搜 "程序精选"关注