当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,JDK中提供了4中拒绝策略:
\(\circ\) CallerRunsPolicy 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
\(\circ\) AbortPolicy 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
\(\circ\) DiscardPolicy该策略下,直接丢弃任务,什么都不做。
\(\circ\) DiscardOldestPolicy 该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列
条件队列条件队列使得一组线程(称之为等待线程集合)能够通过某种方式来等待特定的条件变成真。传统队列的元素是一个个数据,而与之不同的是,条件队列中的元素是一个个正在等待相关条件的线程。
正如每个Java对象都可以作为一个锁,每个对象同样可以作为一个条件队列,并且Object中wait、notify和notifyAll方法就构成了内部条件队列的API。对象的内置锁与其内部条件队列是相互关联的,要调用对象X中条件队列的任何一个方法,必须持有对象X上的锁。这就是因为“等待由状态构成的条件”与“维护状态一致性”这两种机制必须被紧密地绑定在一起:只有能对状态进行检查时,才能在某个条件上等待,并且只有能修改状态时,才能从条件等待中释放一个线程。
当使用条件等待时(例如Object.wait和Condition.await)
通常都有一个条件谓词,包括一些对象状态的测试,线程在执行前必须首先通过这些测试
在调用wait之前测试条件谓词,并且从wait中返回是再次进行测试
在一个循环中调用wait
确保使用与条件队列相关的锁来保护构成条件谓词的各个状态变量
当调用wait、notify或notifyAll等方法时,一定要持有与条件队列相关的锁
在检查条件谓词之后以及开始执行相应的操作之前,不要释放锁
降低锁竞争程度的几种方式减少锁的持有时间
降低锁的请求频率
使用带有协调机制的独占锁,这些机制允许更高的并发性
CAS操作CAS包含3个操作数:需要读写的内存位置V、进行比较的值A和拟写入的新值B。当且仅当V的值等于A时,CAS才会通过原子方式用新值B来更新V的值,否则不会执行任何操作。无论位置V的值是否等于A,都将返回V原有的值。
CAS的主要缺点是:它将使调用者处理竞争问题(通过重试、回退、放弃),而在锁中能自动处理竞争问题,同时CAS还会出现ABA的问题。
Java内存模型(JMM)在共享内存的多处理器体系架构中,每个处理器都拥有自己的缓存,并且定期地与主内存进行协调。在不同的处理器架构中提供了不同级别的缓存一致性(Cache Coherence),其中一部分只提供最小的保证,即允许不同的处理器在任意时刻从同一个存储位置上看到不同的值。操作系统、编译器以及运行时(有时甚至包括应用程序)需要弥合这种硬件能力与线程安全需求之间的差异。
Java内存模型是通过各种操作来定义的,包括对变量的读写操作,监视器的加锁和释放操作,以及线程启动和合并操作。JMM为程序中所有的操作定义了一个偏序关系,称之为Happens-Before。如果两个操作之间缺乏Happens-Before关系,那么JVM可以对它们任意的重排序。
当一个变量被多个线程读取并且至少被一个线程写入时,如果在读操作和写操作之间没有依照Happens-Before来排序,那么就会产生数据竞争的问题。在正确同步的程序中不存在数据竞争,并会表现出串行一致性,这意味着程序中的所有操作都会按照一种固定的和全局的顺序执行。
Happens-Before的规则包括:
程序顺序规则。如果程序中操作A在操作B之前,那么在线程中A操作将在B操作之前执行。
监视器锁规则。在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前执行。
volatile变量规则。 对volatile变量的写入操作必须在对该变量的读操作之前执行。
线程启动规则。在线程上对Thread.start()的调用必须在该线程中执行任何操作之前执行。