Java并发
线程状态
- new:初始化状态,还没start()
- runnable:java中的状态,包括操作系统的ready、running
- time_waiting:超时等待,sleep、wait(timeout)、join(timeout)、parkNano、parkUntil()、await(timeout)会进入这个状态,在一段时间内的等待
- waiting:等待状态,这个状态表示线程需要等待其他线程的一些通知才会返回runnable状态。比如:通知或者中断
- blocked:阻塞状态,锁阻塞
- terminated:终止
线程状态变换
线程有两种资源:CPU时间片、锁资源
wait:让出CPU和锁
sleep:让出CPU
yield:让出CPU,由于线程优先级的存在,让出CPU后有可能马上又获取到CPU时间片
synchronized:让出CPU
stateDiagram-v2 NEW: NEW(创建) RUNNABLE:RUNNABLE(就绪) RUNNING : 运行态(RUNNING) BLOCKED: BLOCKED(同步阻塞) WAITING:WAITING(等待阻塞) TIMED_WAITTING: TIMED_WAITTING(延时等待阻塞) TERMINATED:TERMINATED(终止) NEW-->RUNNABLE:start() RUNNABLE-->RUNNING:OS调度获取CPU时间片 RUNNING-->RUNNABLE:yield释放CPU时间片 RUNNING-->BLOCKED:synchronized RUNNING-->WAITING:wait(),park(),join() BLOCKED-->RUNNABLE:synchronized获到锁 WAITING-->RUNNABLE:notify(),unpark() TIMED_WAITTING-->RUNNABLE:timeout时间到,join线程结束 RUNNING-->TIMED_WAITTING:sleep(timeout),join(timeout) RUNNING-->TERMINATED:run()结束
LockSupport
- LockSupport是jdk提供的阻塞线程的原语,通过二元信号量做的阻塞,park方法可以让线程阻塞,unpark可以让某个线程停止阻塞接入就绪状态。
- LockSupport不支持重入
- LockSupport操作对象是《线程对象》
sleep,wait,yield,await,LockSupport.park区别
- 都会是线程进入waiting状态
- sleep、yield、LockSupport.park**
不会释放锁。wait、await会释放锁和锁能直接关联**; - 如果在wait()之前执行了notify(),wait()
将一直阻塞,因为后续将没有其它notify()唤醒它。 - 如果在park()之前执行了unpark()
线程不会被阻塞,直接跳过park(),继续执行后续内容 - LockSupport.park()
只负责阻塞当前线程,释放锁资源实际上是在Condition的await()方法中实现的 - park()/unpark()底层的原理是“
二元信号量”,像只有一个许可证的Semaphore,只不过在重复执行unpark()的时也不会再增加许可证,最多只有一个许可证。
产生死锁条件以及如何避免死锁
- 条件:
- 存在互斥量资源;
- 存在线程申请资源和保持资源;
- 线程已有的资源不能被剥夺,只能主动释放;
- 线程间存在资源循环等待的闭环;
- 避免死锁:
- 破坏申请\保持资源:资源一次性申请,要么全申请到,要么全申请不到
- 破坏不可剥夺:线程申请不到某个资源,则先释放已有资源
- 破坏循环等待:对资源进行标记序号,线程申请资源时只能按照顺序申请
- java如何查询死锁
- jps查进程号
- jstack查看执行线程栈
实现同步的方法:
- 使用synchronized关键字为方法或代码块加锁。
- 使用volatile修饰变量,但是volatile不保证原子性。
- 使用ReentrantLock或者ReentrantReadWriteLock, 这种方法比synchronized更灵活。
- 使用Semaphore,允许最多n个线程同时访问资源。