并发
线程状态
Debug调试,线程模式。
-
java:6种状态
- NEW(新建)—— start
- RUNNABLE(就绪 | 运行 | 阻塞I/O)—— cpu 调度
- TERMINATED(终结)—— 代码执行完毕
- BLOCKED(阻塞)—— 获取锁失败
- WAITING(等待) ——获得锁的wait()
- TIMED_WAITING(等待有时限) ——获得锁的wait(long)或者sleep(long)
-
操作系统:5种
- 新建
- 就绪(可以分到CPU时间)
- 运行(分到CPU时间)
- 终结
- 阻塞(分不到CPU时间)
线程池7个参数
- corePoolSize ——核心线程
- maximumPoolSize ——最大线程(包含救急线程)
- keepAliveTime——生存时间(空闲救急线程生存时间)
- unit——时间单位
- workQueue——阻塞队列(核心线程满后,放入任务队列,任务队列满启用救急线程)
- threadFactory——线程工厂
- handler——拒绝策略(当核心线程满,任务队列满,救急线程满时,拒绝任务策略)
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(
2,
3,
0,
TimeUnit.MILLISECONDS,
workQueue,
r -> new Thread(r,"myThread"+c.getAndIncrement()),
new ThreadPoolExecutor.AbortPolicy()
);
拒绝策略
//AbortPolicy():抛出异常
//CallerRunsPolicy():由调用者执行
//DiscardPolicy():丢弃任务
//DiscardOldestPolicy():丢弃队列中最老任务
sleep VS wait
共同点:都会暂时放弃CPU使用权,进入阻塞状态。
不同点:
- 方法归宿不同
- sleep是Thread的静态方法。
- wait是Object成员方法。每个对象都有。
- 醒来时机不同
- wait(long) 和wait() 能被notify唤醒
- 都可以被打断唤醒。
- 锁特性不同
- wait方法必须先获取wait对象锁,sleep则不用
- wait方法会释放锁,允许其他线程获取锁。
- sleep如果在synchronized代码块中执行,并不会释放对象锁。
线程同步
synchronized :可以使用wait、notify实现。
lock:可以使用条件变量实现。
lock VS synchronized
- 语法层面:
- synchronized 是关键字,源码在jvm中,C++实现。
- Lock是接口,源码jdk提供,Java实现。
- synchronized会自动释放锁;Lock手动释放锁。
- 功能层面
- 都是悲观锁,具备基本的互斥、同步、锁重入功能。
- Lock提供许多synchronized 不具备的功能,如获取等待状态、公平锁、可打断、可超时、多条件变量。
- Lock适合不同场景的实现,ReentrantLock、ReentrantReadWritreLock。
- 性能层面
- 在没有竞争时,synchronized做了很多优化,偏向锁、轻量级锁。
- 在竞争激烈时,Lock有更好的性能。
公平与非公平
条件变量
- await()
会释放锁,并把线程加入等待队列(waiting queue)中。 - signal()
唤醒waiting queue中线程,加入block queue阻塞队列最后。
volatile能否保证线程安全?
线程安全3个方面:可见性、有序性、原子性。
不能,volatile只能保证可见性、有序性。
可见性原因、解决
原因:当一段代码执行次数极多时,会被JIT即时编译器优化,替换,下次执行JIT优化后的代码。导致不能读取已经更改的值。
解决:
- 不使用JIT热点代码的优化:-Xint;
- volatile修饰,保证可见性。
volatile位置
写操作:volatile的变量放在最后,保证上面写操作都被写屏障保护,保障不会出现先赋值在写操作。
读操作:volatile的变量放在第一个,保证下面都被读屏障保护,保障不会读取到未赋值的变量。
悲观锁与乐观锁
悲观锁:
- 就是线程必须占有了锁才能操作共享变量,每次只有一个线程占锁成功,获得锁失败的线程都要等待。
- 线程会从运行到阻塞频繁上下文切换,影响性能。
- synchronized和lock锁都会有几次重试操作,减少阻塞。
乐观锁:
- 无需加锁,每次只有一个线程修改共享数据成功,其他失败线程不用停止,不断尝试直到成功。
- 线程是一直运行,不涉及线程上下文切换。
- 多核CPU,线程数不应超过cpu核数。