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如何查询死锁
    1. jps查进程号
    2. jstack查看执行线程栈

实现同步的方法:

  • 使用synchronized关键字为方法或代码块加锁。
  • 使用volatile修饰变量,但是volatile不保证原子性。
  • 使用ReentrantLock或者ReentrantReadWriteLock, 这种方法比synchronized更灵活。
  • 使用Semaphore,允许最多n个线程同时访问资源。