多线程的基本概念
基本概念
- Java多线程内容:
- Object类中的wait(),notify() 等接口
- Thread类中的接口
- synchronized关键字
- JUC是指java.util.concurrent包
线程状态图
-
Java中线程的状态分为6种: 新建状态New, 运行状态Runnable( 包括就绪状态ready和运行状态running), 阻塞状态Blocked, 等待状态WAITING, 超时等待状态 (TIMED_WAITING), 终止状态TERMINATED(消亡状态Dead)
-
新建状态NEW: 新创建了一个线程对象,但还没有调用start() 方法
- 线程对象被创建后,就进入了新建状态
- 实现Runnable接口和继承Thread可以得到一个线程类 ,new一个实例出来,线程就进入了初始状态
- 比如 : Thread thread = new Thread();
-
运行状态RUNNABLE: Java线程中将就绪状态Ready和运行状态Running统称为运行状态Runnable
- 就绪状态Ready: 线程对象创建后,其余线程调用该对象的start() 方法.该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态
- 调用线程的start() 方法,此线程进入就绪状态
- 当前线程sleep() 方法结束,其余线程join() 结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态
- 当前线程时间片用完了,调用当前线程的yield() 方法,当前线程进入就绪状态
- 锁池里的线程拿到对象锁后,进入就绪状态
- 就绪状态只是表示线程可以运行,如果调度程序调度到线程,线程就永远处于就绪状态
- 运行状态Running: 就绪状态的线程在获得CPU时间片后变为运行状态
- 线程调度程序从可运行池中选择一个就绪状态Ready的线程进行调度
- 这是线程进入运行状态的唯一方式
- 就绪状态Ready: 线程对象创建后,其余线程调用该对象的start() 方法.该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态
-
阻塞状态BLOCKED: 线程阻塞于锁
- 线程阻塞在进入synchronized关键字修饰的方法或代码块获取锁时的状态
-
等待状态WAITING: 进入该状态的线程需要等待其余线程做出一些特定动作,比如通知或中断
- 处于这种状态的线程不会被分配CPU执行时间
- 等待被显式地唤醒,否则会处于无限期等待的状态
-
超时等待状态TIMED_WAITING: 该状态不同于WAITING, 可以在指定的时间后自行返回
- 处于这种状态的线程不会被分配CPU执行时间
- 不需要无限期等待被其余线程显式地唤醒,在达到一定时间后会自动唤醒
-
终止状态TERMINATED: 线程已经执行完毕
- 当线程的run() 方法完成时,或者主线程的main() 方法完成时,就认为线程终止
- 线程对象也许是活的,但是已经不是一个单独执行的线程.线程一旦终止,就不能复生
- 在一个终止的线程上调用start() 方法,会抛出java.lang.IllegalThreadStateException异常
等待队列
- 调用obj的wait(),notify() 方法前,必须获得obj锁.即wait() 和notify() 方法必须写在synchronized(obj) 代码内
- 线程1获取对象的锁,正在使用对象A
- 线程1调用对象A的wait() 方法
- 线程1释放对象A的锁,进入等待队列
- 同步队列的线程争抢对象锁
- 线程5获取对象的锁,使用对象A
- 线程5调用A对象的notifyAll() 方法唤醒所有等待线程
- notifyAll() 方法所在的synchronized结束,线程5释放对象A的锁
- 同步队列的线程争抢对象锁
Object类
- wait()
- notify()
- notifyAll()
Thread类
- 线程休眠函数 : sleep()
- 线程中断函数 : interrupt()
- 获取线程名称 : getName()
synchronized关键字
- 分为synchronized代码块和synchronized方法
- 作用: 使得线程获取对象的同步锁
同步队列
- 当前线程调用对象A的同步方法时,发现对象A的锁被其余线程占用,此时当前线程进入同步队列. 即同步队列中的线程都是准备争抢对象锁的线程
- 当一个线程1被另外一个线程2唤醒时,线程1进入同步队列,去争抢对象锁
- 同步的环境下才会有同步队列.一个对象对应一个同步队列
- 超时等待线程到了等待时间或者等待线程被notify() 方法或者notifyAll() 方法唤醒,会进入同步队列中去竞争锁:
- 如果获得锁,则进入运行状态RUNNABLE
- 如果没有获得锁,则进入阻塞状态BLOCKED等待获取锁
Thread中的方法
Thread.sleep(long millis)
- 作用: 给其余线程执行的机会
- 一定是当前线程调用此方法
- 当前线程进入超时等待状态TIMED_WAITING, 不释放对象锁, millis后线程自动苏醒进入就绪状态
Thread.yield()
- 作用:
- 使得相同优先级的线程轮流执行,但是并不保证相同优先级的线程一定会轮流执行
- 实际中无法保证yield() 达到让步目的,因为让步的线程还有可能会被线程调度程序再次选中
- Thread.yield() 不会导致线程阻塞,该方法与sleep() 类似,只是不能由用户指定暂停多长时间
- 一定是当前线程调用此方法
- 当前线程放弃获取的CPU时间片,不释放资源,由运行状态Running转变为就绪状态Ready, 重新选择执行线程
thread.join
- 包括 : thread.join() 和 thread.join(long millis)
- 当前线程调用其余线程的join方法,当前线程进入等待状态WAITING或者超时等待状态TIMED_WAITING. 当前线程不会释放已经持有的对象锁
- 调用的其余线程执行完毕或者超时等待时间millis时间结束,当前线程进入RUNNABLE状态,也可能进入BLOCKED状态
obj.wait()
- 当前线程调用对象的wait() 方法,当前线程释放对象锁,进入等待队列
- 进入等待队列的线程依靠notify() 或者notifyAll() 唤醒
- 如果是调用对象的超时等待方法wait(long timeout), 则超时等待时间timeout时间结束自动唤醒
obj.notify()
- 唤醒此对象监视器上等待的单个线程,选择是任意性的
obj.notifyAll()
- 唤醒在此对象监视器上等待的所有线程
LockSupport
- 包括LockSupport.park(), LockSupport.parkNanos(long nanos), LockSupport.parkUntil(long deadlines)
- 当前线程进入等待状态WAITING或者超时等待状态TIMED_WAITING
- 不需要获得锁就可以让线程进入等待状态WAITING或者超时等待状态TIMED_WAITING
- 需要通过LockSupport.unpark(Thread thread) 方法唤醒线程