方圆

thread-and-competition

2019-07-14

Java 线程

线程的实现

  1. 使用内核线程实现:程序使用内核线程(KLT)的高级接口-轻量级进程(LWP),LWP与KLT是1:1的关系.
  2. 使用用户线程实现:用户线程(UT)内核无法感知,与LWP是1:n的关系;
  3. 混合实现:与LWP是n:m的关系。
  4. 线程模型只对线程的并发规模和操作成本产生影响,对于编码和运行来说是透明的。Sun JDK的Windows和Linux版本都是使用1:1的此线程模型实现。

    Java 线程调度

    线程调度分为协同式线程调度和抢占式线程调度。
  • 协同式:线程的执行时间由线程本身决定。
  • 抢占式:系统分配执行时间。

Java的线程调度是系统自动完成,但可以通过设置线程优先级建议系统。在两个线程同时处于Ready时,优先级越高的线程越容易被选中执行。但由于Java的线程最终映射到操作系统原生线程,线程调度最终取决于操作系统。

状态转换

Java语言定义了5种线程状态,在任意时间点,一个线程只能有其中一个状态。

  • New:创建后尚未启动;
  • Runable:可能在执行,可能在等待CPU分配执行时间。对应操作系统线程状态的 Running和Ready。
  • Waiting:无限期等待,在其他线程显示的唤醒之前不会被分配CPU时间。
    • 没有设置Timeout参数的Object.wait();
    • 没有设置Timeout参数的Thread.join();
    • LockSupport.park();
  • Timed Waiting:到达时间后无需其他线程唤醒,自动唤醒。
    • Thread.sleep();
    • 设置了Timeout参数的Object.wait();
    • 设置了Timeout参数的Thread.join();
    • LockSupport.parkNanos();
    • LockSupport.parkUntil();
  • Blocked:等待获取一个排它锁,在程序等待进入同步区域时,线程进入这种状态。
  • Terminated:已终止的线程。

    竞争状态

  • 步骤
    1. 线程1获取对象A的锁,正在使用对象A。
    2. 线程1调用对象A的wait()方法。
    3. 线程1释放对象A的锁,并马上进入等待队列。
    4. 锁池里面的对象争抢对象A的锁。
    5. 线程5获得对象A的锁,进入synchronized块,使用对象A。
    6. 线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入同步队列。若线程5调用对象A的notify()方法,则唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入同步队列。
    7. notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
    8. 同步队列的线程争抢对象锁,但线程1什么时候能抢到就不知道了。 
  • 同步队列状态
    • 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入同步队列。简言之,同步队列里面放的都是想争夺对象锁的线程。
    • 当一个线程1被另外一个线程2唤醒时,1线程进入同步队列,去争夺对象锁。
    • 同步队列是在同步的环境下才有的概念,一个对象对应一个同步队列。
    • 线程等待时间到了或被notify/notifyAll唤醒后,会进入同步队列竞争锁,如果获得锁,进入RUNNABLE状态,否则进入BLOCKED状态等待获取锁。
  • 几个方法的比较
    1. Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,millis后线程自动苏醒进入就绪状态。作用:给其它线程执行机会的最佳方式。
    2. Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。
    3. thread.join()/thread.join(long millis),当前线程里调用其它线程t的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。
    4. obj.wait(),当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列。依靠notify()/notifyAll()唤醒或者wait(long timeout) timeout时间到自动唤醒。
    5. obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
    6. LockSupport.park()/LockSupport.parkNanos(long nanos),LockSupport.parkUntil(long deadlines), 当前线程进入WAITING/TIMED_WAITING状态。对比wait方法,不需要获得锁就可以让线程进入WAITING/TIMED_WAITING状态,需要通过LockSupport.unpark(Thread thread)唤醒。
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章