如果你提交任务时,线程池队列已满,这时会发生什么?

这里区分一下:

1、如果使用的是无界队列 LinkedBlockingQueue,也就是无界队列的话,没关

系,继续添加任务到阻塞队列中等待执行,因为 LinkedBlockingQueue 可以近乎

认为是一个无穷大的队列,可以无限存放任务

2、如果使用的是有界队列比如 ArrayBlockingQueue,任务首先会被添加到

ArrayBlockingQueue 中,ArrayBlockingQueue 满了,会根据

maximumPoolSize 的值增加线程数量,如果增加了线程数量还是处理不过来,

ArrayBlockingQueue 继续满,那么则会使用拒绝策略

RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy

高并发、任务执行时间短的业务怎样使用线程池?

高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

并发不高、任务执行时间长的业务怎样使用线程池?

并发不高、任务执行时间长的业务要区分开看: a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务 b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换

并发高、业务执行时间长的业务怎样使用线程池?

并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步, 至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。

synchronized的CPU原语级别是如何实现的?

代码片段

synchronized代码块主要是靠monitorenter和monitorexit这两个原语来实现同步的。当线程进入monitorenter获得执行代码的权利时,其他线程就不能执行里面的代码,直到锁Owner线程执行monitorexit释放锁后,其他线程才可以竞争获取锁。

普通方法

常量池中多了ACC_SYNCHRONIZED标示符。JVM就是根据该标示符来实现方法的同步的:当方法调用时会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。这种方式与语句块没什么本质区别,都是通过竞争monitor的方式实现的。只不过这种方式是隐式的实现方法。

静态方法

常量池中用ACC_STATIC标志了这是一个静态方法,然后用ACC_SYNCHRONIZED标志位提醒线程去竞争monitor。由于静态方法是属于类级别的方法(即不用创建对象就可以被调用),所以这是一个类级别(XXX.class)的锁,即竞争某个类的monitor。

monitor介绍

1.每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

2.如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.这里涉及重入锁,如果一个线程获得了monitor,他可以再获取无数次,进入的时候monito+1,退出-1,直到为0,开可以被其他线程获取

3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

什么时候触发MinorGC?什么时候触发FullGC?

summerZBH123 2018-07-23 21:28:21 5172 收藏 4 分类专栏: jvm 触发MinorGC(Young GC) 虚拟机在进行minorGC之前会判断老年代最大的可用连续空间是否大于新生代的所有对象总空间

1、如果大于的话,直接执行minorGC

2、如果小于,判断是否开启HandlerPromotionFailure,没有开启直接FullGC

3、如果开启了HanlerPromotionFailure, JVM会判断老年代的最大连续内存空间是否大于历次晋升的大小,如果小于直接执行FullGC

4、如果大于的话,执行minorGC

触发FullGC 老年代空间不足 如果创建一个大对象,Eden区域当中放不下这个大对象,会直接保存在老年代当中,如果老年代空间也不足,就会触发Full GC。为了避免这种情况,最好就是不要创建太大的对象。

持久代空间不足 如果有持久代空间的话,系统当中需要加载的类,调用的方法很多,同时持久代当中没有足够的空间,就出触发一次Full GC

YGC出现promotion failure promotion failure发生在Young GC, 如果Survivor区当中存活对象的年龄达到了设定值,会就将Survivor区当中的对象拷贝到老年代,如果老年代的空间不足,就会发生promotion failure, 接下去就会发生Full GC.

统计YGC发生时晋升到老年代的平均总大小大于老年代的空闲空间 在发生YGC是会判断,是否安全,这里的安全指的是,当前老年代空间可以容纳YGC晋升的对象的平均大小,如果不安全,就不会执行YGC,转而执行Full GC。

显示调用System.gc

服务器端接受多个请求时的高并发处理 多个RPC请求进来,服务器怎么处理并发呢

1.在服务器端编程处理时,可以设置一个全局的队列变量来存储从客户端传过来的参数。这个全局队列存储了多个客户端发送过来的参数,处理程序要处理时直接从这个队列中读取就可以了。服务器端接受客户端请求向共享队列中写参数作为一个线程,读取队列中的参数并进行处理作为另一个线程。两个线程独立执行来提高处理的并发性。

附:服务器的同步处理与异步处理的分析

同步服务为每个请求创建单一线程,由此线程完成整个请求的处理:接收消息,处理消息,返回数据;这种情况下服务器资源对所有入栈请求开放,服务器资源被所有入栈请求竞争使用,如果入栈请求过多就会导致服务器资源耗尽宕机,或者导致竞争加剧,资源调度频繁,服务器资源利用效率降低。

异步服务则可以分别设置两个线程队列,一个专门负责接收消息,另一个专门负责处理消息并返回数据,另有一些值守线程负责任务派发和超时监控等工作。在这种情况下无论入栈请求有多少,服务器始终依照自己的能力处理请求,服务器资源消耗始终在一个可控的范围。这种模式的一个问题就是这两个线程队列的大小如何根据机器负载情况动态调整。

对于异步服务模式:

这种情况下,虽然入栈请求以消息队列的方式被异步处理但每个请求内部却是采用阻塞的方式访问外部资源,如果外部资源访问速度过慢,可能导致请求处理队列中的所有线程均处于阻塞状态,此时CPU使用率虽然很低但是却因为队列中线程已满而无法处理消息队列中的新消息,此时若能调整线程队列最大线程数将可提高CPU利用率。但另一个问题是如果线程数被调高之后所有线程的IO处理突然结束并且接下来每个线程都将进行大量计算的话那么CPU可能出现过载。

在系统运行的每个时间点上,当时正在进行IO的线程数量和正在进行计算的线程数量是不断变化着的,那么如何才能设计出一个可以根据系统当时情况自动适应负载变化的高度自适应的系统呢?

在这方面采用反应式计算模型确实能设计出适应负载能力很强的系统,系统利用率和吞吐量可以大幅提高,但这种系统仍然可能会出现系统局部负载过高的风险。

采用反应式计算模型,不仅系统中的入栈请求以消息队列的方式得以异步化,而且系统中所有的IO任务也必需依照此法行之,这些IO任务的处理需要采用异步模型(如NIO)。另外要考虑的就是如何划分异步IO消息并为其配置线程队列了,比如是要将所有IO任务放入统一的队列还是为某类IO任务设置单独的队列。

服务器资源虽然由系统分配但大多以线程为持有者被线程持有并使用,如线程堆栈,被线程持有的各类锁等资源。

Sentinel的工作方式:

1):每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令。

2):如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。

3):如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。

4):当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 。

5):在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令 。

6):当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次 。

7):若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。

若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

心跳检测

在命令传播阶段,从服务器默认以每秒一次的频率,向主服务器发送命令:

REPLCONF ACK //replication_offset是从服务器当前的复制偏移量。

心跳检测的作用:检测主服务器的网络连接状态;辅助实现min-slaves选项;检测命令丢失。

检测主从服务器的网络连接状态

通过向主服务器发送INFO replication命令,可以列出从服务器列表,可以看出从最后一次向主发送命令距离现在过了多少秒。

lag的值应该在0或1之间跳动,如果超过1则说明主从之间的连接有故障。

辅助实现min-slaves选项

Redis可以通过配置防止主服务器在不安全的情况下执行写命令;

min-slaves-to-write 3

min-slaves-max-lag 10

上面的配置表示:从服务器的数量少于3个,或者三个从服务器的延迟(lag)值都大于或等于10秒时,主服务器将拒绝执行写命令。这里的延迟值就是上面INFOreplication命令的lag值。

检测命令丢失

如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么当从服务器向主服务器发送REPLCONF ACK命令时,主服务器将发觉从服务器当前的复制偏移量少于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里面找到从服务器缺少的数据,并将这些数据重新发送给从服务器。

主服务器向从服务器补发缺失数据这一操作的原理和部分重同步操作的原理非常相似,它们的区别在于:补发缺失数据操作在主从服务器没有断线的情况下执行,而部分重同步操作则在主从服务器断线并重连之后执行。