疏漏总结(五)

1、线程池的最大核心线程数的意义

  1. 提交一个新的任务到线程池中首先线程池会判断基本线程池(corePoolSize)是否已满?没满的话会创建一个工作线程来执行任务。满了进入下个流程;其次线程池判断工作队列(workQueue)是否已满?没满的话将新提交的任务存储在工作队列里面,满了进入下个流程;最后线程池判断整个线程池(maxmumPoolSize)是否已满?没满的话创建一个新的工作线程来执行任务,满了则交给饱和策略来处理这个任务;如果线程池中的线程数量大于corePoolSize时如果某线程空闲时间超过keepAliveTime,线程会被终止,直至线程池中的线程数目不大于corePoolSize;如果允许为核心池中的线程设置存活时间那么核心池中的线程空闲时间超过keepAliveTime线程会被终止。
  2. 饱和策略
    Abort策略:默认策略,新任务提交时直接抛出未检查的异常RejectedExecutionException该异常可由调用者捕获。
    CallerRuns策略:为调节机制既不抛弃任务也不抛出异常,而是将某些任务回退到调用者。不会在线程池的线程中执行新的任务,而是在调用exector的线程中运行新的任务。
    Discard策略:新提交的任务被抛弃。

2、线程池的状态

在这里插入图片描述
RUNNING
1、状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。
2、状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!

SHUTDOWN
1、状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。
2、状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。

STOP
1、状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。
2、 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。

TIDYING
1、状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。
2、状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。
线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。

TERMINATED
1、状态说明:线程池彻底终止,就变成TERMINATED状态。
2、状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。

3、http 1.1版本新特性

  1. 持久连接: 在HTTP1.1之前,无论什么时候浏览器连接一个web服务器,当请求资源被发送之后,连接就被服务器端关闭了。随着互联网进一步发展,请求的服务器资源种类越来越多,资源的越来越大,所以当一个页面被请求的时候,浏览器需要下载页面所引用的资源。如果加入页面和所引用的资源使用不同的连接来下载的话,进程将会非常慢。HTTP1.1就是为了解决这个问题,使用持久连接的时候,服务器并不关闭连接。这种情况下,页面和所引用的资源使用同一个连接来下载,考虑到建立和接触http连接的宝贵时间的话,这样就节约了大量的时间。
  2. 块编码:建立持续连接的结果就是,同一个连接,服务器端可以从不同的资源发送字节流,客户端可以使用发送多个请求。这样发送端必须为每个请求或者响应发送内容长度的头部,以便知道收方如何解析这些字节。在content-length头部不知道的情况下,在HTTP1.0中,服务器可以省略content-length头部,并保持写入连接,当写入完成的时候,它将简单的关闭连接。客户端将会保持读取状态,直到读取到-1,表示达到文件的尾部。在HTTP1.1中,使用一个特别的头transfer-encoding来表示有多少块形式的字节流将会被发送。对于每块来说,在数据之前,长度(十六进制)后边跟着CR/LF将被发送。整个事务通过一个零长度块来标识。0\r\n标识这个事务的结束。
  3. 状态100(持续状态)的使用:在发送请求之前,HTTP 1.1客户端可以发送Expect: 100-continue头部到服务器,并等待服务器的确认。。这个一般发生在当客户端需要发送一份长的请求内容而未能确保服务器愿意接受它的时候。如果你 发送一份长的请求内容仅仅发现服务器拒绝了它,那将是一种浪费来的。当接受到Expect: 100-continue头部的时候,假如乐意或者可以处理请求的话,服务器响应100-continue头部,后边跟着两对CRLF字符。 HTTP/1.1 100 Continue。

4、何时需要rehash

单线程的ReHash

  • 用key mod一下表的大小(就是数组的长度)
  • 最上面的old hash表,其中Hash表的size=2,key=3,7,5在mod 2之后都冲突在table[1]这里面
  • 接下来三个步骤是Hash表热size成4,然后所有的<key, value>重新rehash过程

在这里插入图片描述
并发下的ReHash
1、假设现在有两个线程,使用红色和浅蓝色标记一下:

do{
    Entry<K,V> next = e.next;// <--假设线程一执行到这里就被调度挂起了
    inti = indexFor(e.hash, newCapacity);
    e.next = newTable[i];
    newTable[i] = e;
    e = next;
}while (e != null);

线程二执行完毕后,观看下面例子:
在这里插入图片描述
注意因为Thread1的e指向key(3),next指向key(7),其在线程二rehash后指向线程二重组的链表。

2、线程一被调用回来执行

  • 先执行new Table[i]=e;
  • 然后是e=next,导致e指向key(7)
  • 下一次循环的next=e.next导致next指向key(3)
    在这里插入图片描述
    3、线程一继续执行
    把key(7)摘下来,放到newTable[i]的第一个,然后把e和next往下移
    在这里插入图片描述
    4、环形链表出现
    e.next = newTable[i] 导致 key(3).next 指向了 key(7)
    注意:此时的key(7).next 已经指向了key(3), 环形链表就这样出现
    在这里插入图片描述
    如果这时线程一调用到HashTable.get(11)就会出现——Infinite Loop。

5、Full GC触发条件

  1. System.gc()的调用
  2. 老年代空间不足
  3. 永久代空间不足
  4. CMS GC时出现promotion failed和concurrent mode failure
  5. 堆中分配很大的对象

6、何种场景需要自定义类加载器

  1. 修改类加载方式:类的加载模型并非强制,除Bootstrap外,其它的加载并非一定要引入,或者根据实际情况在某个时间点进行按需动态加载。
  2. 扩展加载源:比如从数据库、网络,甚至是电视机机顶盒进行加载。
  3. 防止源码泄露。java代码容易被编译和篡改,可以进行编译加密。那么类加载器也需要自定义,还原加密的字节码。
  4. 隔离加载类:在某些框架内进行中间件与应用的模块隔离,把类加载到不同的环境。比如,某容器框架通过自定义类加载器确保应用中依赖的jar包不会影响到中间件运行时使用的jar包。(jar包之间的冲突的消除)

实现方法:继承ClassLoader,重写findClass()方法,调用defineClass()方法。

7、分段和分页区别

1、目的
页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率。或者说分页是处于系统管理的需要而不是用户需要。

段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了更好地满足用户的需要。

2、长度
页的大小固定而且由系统决定,由系统把逻辑地址划分为页号和页内地址两部分,是由机器硬件实现的,因而在系统中只能有一种大小的页面。

段的长度不固定,决定于用户所编写的程序,通常由编译程序在对程序进行编译时,根据信息的性质来划分。

3、地址空间
页的地址空间是一维的,即单一的线形地址空间,程序员只要利用一个记忆符就可以表示一个地址。

作业地址空间是二维的,程序员在标识一个地址时,既需要给出段名,又需给出段内地址。

4、碎片
分页有内部碎片无外部碎片

分段有外部碎片无内部碎片

5、绝对地址
处理器使用页号和偏移量计算绝对地址

处理器使用段号和偏移量计算绝对地址

6、管理方式
对于分页,操作系统必须为每个进程维护一个页表,以说明每个页对应的的页框。当进程运行时,它的所有页都必须在内存中,除非使用覆盖技术或虚拟技术,另外操作系统需要维护一个空闲页框列表。

对于分段,操作系统必须为每个进程维护一个段表,以说明每个段的加载地址和长度。当进程运行时,它的所有短都必须在内存中,除非使用覆盖技术或虚拟技术,另外操作系统需要维护一个内存中的空闲的空洞列表。

特别的当使用虚拟技术时候把一页或一段写入内存时候可能需要把一页或几个段写入磁盘。

7、共享和动态链接
分页不容易实现,分段容易实现。

8、最左前缀原则

MySQL中的索引可以以一定顺序引用多列这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:

select * from user where name=xx and city=xx ; //可以命中索引
select * from user where name=xx ; // 可以命中索引
select * from user where city=xx ; // 无法命中索引            

注意查询的时候如果两个条件都用上了,但是顺序不同,如 city= xx and name =xx,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。

由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。

9、G1

此垃圾收集器适用于新生代和老年代,和其他老年代垃圾收集器相比有如下的特点:

  • 并发和并行:使用多个CPU来减少stop-the-world停止时间,多线程是并发执行的;
  • 分代收集:使用不同方式去处理,新创建出来的对象产生垃圾和已存在一段时间的对象产生的垃圾;
  • 空间整合:通过标记整理解决内存碎片的问题;
  • 可预测的停顿:使用户指定在GC上消耗的时间,不超过设定的时间。

使用此垃圾收集器之后整个Java堆内存被划分成多个大小相等的Region,新生代和老年代不是物理隔离的。
在这里插入图片描述
不需要一个连续的内存空间决定哪个是新生代哪个是老年代。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章