一、线程有哪些状态
1. Java层面线程分为六种状态
新建、可运行、终结、阻塞、等待、有时限等待
新建:当新建一个线程时,它没有和操作系统底层正在的线程关联起来,所以此时不会被cpu分配内存执行代码,只有调用线程的start方法变成RUNNABLE后才会与操作线程关联起来,交给CPU运行处理。只有RUNNABLE状态才会被分配CPU,其它状态不会被交给CPU执行相应的代码。
阻塞:当线程运行时会出现多个线程去争抢同一把锁,只有一个线程能成功,失败的线程就会变成BLOCKED阻塞状态。
等待:当一个线程成功抢到锁后,可能有一些条件不符合,还要去执行相应的条件,这个时候可以调用wait()方法,释放锁,进入WAITING等待状态。当将来条件满足了,由另一个线程调用notify()方法可以将等待的线程唤醒。唤醒后还要去重新争取锁,争取成功后就会变成可运行状态。
有时限的等待:等待时设置了一个时间,这种线程除了可以被notify()唤醒,还可以等时间到了被唤醒。还有一种就是线程调用了sleep(long)方法,sleep不会释放锁,就会变成等待状态,时间到了就会被唤醒。
2.操作系统层面有五种状态:
新建、就绪、运行、终结、阻塞
阻塞I/O:线程中有一些操作是由磁盘和网卡单独运行,不需要分配CPU,所以被归为阻塞I/O
二、线程池的核心参数
核心线程:线程池维护的最小线程数量,核心线程创建后不会被回收(注意:设置allowCoreThreadTimeout=true后,空闲的核心线程超过存活时间也会被回收)。
救急线程:会被回收的线程
workQueue:工作队列
存放待执行任务的队列:当提交的任务数超过核心线程数大小后,再提交的任务就存放在工作队列,任务调度时再从队列中取出任务。它仅仅用来存放被execute()方法提交的Runnable任务。工作队列实现了BlockingQueue接口。
handler:拒绝策略
当线程池线程数已满,并且工作队列达到限制,新提交的任务使用拒绝策略处理。可以自定义拒绝策略,拒绝策略需要实现RejectedExecutionHandler接口。
JDK默认的拒绝策略有四种:
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常,默认拒绝策略。
DiscardPolicy:丢弃任务,但是不抛出异常。可能导致无法发现系统的异常状态。
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。
CallerRunsPolicy:由调用线程处理该任务
三、Sleep和Wait
四、lock和synchronized
互斥:所有的线程都去抢同一把锁,但最终只有一个线程能成功
同步:当一个线程抢到锁后,运行代码时,发现要调用另一个线程,那么此时这个线程就会进入等待状态,等另一个线程拿到锁唤醒。
锁重入:一个对象被锁多次,被锁几次就要解锁几次
获取等待状态,Lock有方法可以看到有哪些线程被阻塞了
公平锁和非公平锁:所有线程争抢同一把锁的时候,会有失败的线程,当这些失败的线程再次争抢同一把锁的时候,如果支持先来后到的原则就是公平锁,如果允许插队就是非公平锁。synchronized只支持非公平锁,Lock既支持非公平锁又支持公平锁。如果一个线程一直抢不到锁,Lock支持可打断,可超时,意思就是不抢这个锁了,或者超时了就不强了。但synchronized不支持可打断可超时。
多条件变量:多个等待队列
五、volatile
volatile能否保证线程安全