zoukankan      html  css  js  c++  java
  • Thread详解

    具体可参考:Java并发编程:Thread类的使用,这里对线程状态的转换及主要函数做一下补充。

    一. 线程状态转换图

      注意:

    1. 调用obj.wait()的线程需要先获取obj的monitor,wait()会释放obj的monitor并进入等待态。所以wait()/notify()都要与synchronized联用。详见:JAVA多线程之wait/notify

    1.1 阻塞与等待的区别

    阻塞:当一个线程试图获取对象锁(非java.util.concurrent库中的锁,即synchronized),而该锁被其他线程持有,则该线程进入阻塞状态。它的特点是使用简单,由JVM调度器来决定唤醒自己,而不需要由另一个线程来显式唤醒自己,不响应中断
    等待:当一个线程等待另一个线程通知调度器一个条件时,该线程进入等待状态。它的特点是需要等待另一个线程显式地唤醒自己,实现灵活,语义更丰富,可响应中断。例如调用:Object.wait()、Thread.join()以及等待Lock或Condition。

      需要强调的是虽然synchronized和JUC里的Lock都实现锁的功能,但线程进入的状态是不一样的。synchronized会让线程进入阻塞态,而JUC里的Lock是用LockSupport.park()/unpark()来实现阻塞/唤醒的,会让线程进入等待态。但话又说回来,虽然等锁时进入的状态不一样,但被唤醒后又都进入runnable态,从行为效果来看又是一样的。

    二. 主要操作 

    2.1 start()

    新启一个线程执行其run()方法,一个线程只能start一次。主要是通过调用native start0()来实现。

     1     public synchronized void start() {
     2      //判断是否首次启动
     3         if (threadStatus != 0)
     4             throw new IllegalThreadStateException();
     5 
     6         group.add(this);
     7 
     8         boolean started = false;
     9         try {
    10        //启动线程
    11             start0();
    12             started = true;
    13         } finally {
    14             try {
    15                 if (!started) {
    16                     group.threadStartFailed(this);
    17                 }
    18             } catch (Throwable ignore) {
    19                 /* do nothing. If start0 threw a Throwable then
    20                   it will be passed up the call stack */
    21             }
    22         }
    23     }
    24 
    25     private native void start0();

    2.2 run()

    run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当该线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

    2.3 sleep()

    sleep方法有两个重载版本:

    1 sleep(long millis)     //参数为毫秒
    2  
    3 sleep(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

    sleep相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。

    但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象。

    2.4 yield()

    调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

    注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

    2.5 join()

    join方法有三个重载版本:

    1 join()
    2 join(long millis)     //参数为毫秒
    3 join(long millis,int nanoseconds)    //第一参数为毫秒,第二个参数为纳秒

    join()实际是利用了wait(),只不过它不用等待notify()/notifyAll(),且不受其影响。它结束的条件是:1)等待时间到;2)目标线程已经run完(通过isAlive()来判断)。

     1 public final synchronized void join(long millis) throws InterruptedException {
     2     long base = System.currentTimeMillis();
     3     long now = 0;
     4 
     5     if (millis < 0) {
     6         throw new IllegalArgumentException("timeout value is negative");
     7     }
     8     
     9     //0则需要一直等到目标线程run完
    10     if (millis == 0) {
    11         while (isAlive()) {
    12             wait(0);
    13         }
    14     } else {
    15         //如果目标线程未run完且阻塞时间未到,那么调用线程会一直等待。
    16         while (isAlive()) {
    17             long delay = millis - now;
    18             if (delay <= 0) {
    19                 break;
    20             }
    21             wait(delay);
    22             now = System.currentTimeMillis() - base;
    23         }
    24     }
    25 }

    2.6 interrupt()

    此操作会将线程的中断标志位置位,至于线程作何动作那要看线程了。

    • 如果线程sleep()、wait()、join()等处于阻塞状态,那么线程会定时检查中断状态位如果发现中断状态位为true,则会在这些阻塞方法调用处抛出InterruptedException异常,并且在抛出异常后立即将线程的中断状态位清除,即重新设置为false。抛出异常是为了线程从阻塞状态醒过来,并在结束线程前让程序员有足够的时间来处理中断请求。
    • 如果线程正在运行、争用synchronized、lock()等,那么是不可中断的,他们会忽略。

    可以通过以下三种方式来判断中断:

    1)isInterrupted()

    此方法只会读取线程的中断标志位,并不会重置。

    2)interrupted()

    此方法读取线程的中断标志位,并会重置。

    3)throw InterruptException

    抛出该异常的同时,会重置中断标志位。

    2.7 suspend()/resume()

    挂起线程,直到被resume,才会苏醒。

    但调用suspend()的线程和调用resume()的线程,可能会因为争锁的问题而发生死锁,所以JDK 7开始已经不推荐使用了。

  • 相关阅读:
    创建Variant数组
    ASP与存储过程(Stored Procedures)
    FileSystemObject对象成员概要
    Kotlin 朱涛9 委托 代理 懒加载 Delegate
    Kotlin 朱涛 思维4 空安全思维 平台类型 非空断言
    Kotlin 朱涛7 高阶函数 函数类型 Lambda SAM
    Kotlin 朱涛16 协程 生命周期 Job 结构化并发
    Proxy 代理模式 动态代理 cglib MD
    RxJava 设计理念 观察者模式 Observable lambdas MD
    动态图片 Movie androidgifdrawable GifView
  • 原文地址:https://www.cnblogs.com/waterystone/p/4920007.html
Copyright © 2011-2022 走看看