Thread类作为线程的基类,提供了一系列方法。本文将简单介绍thread各种属性的设置与获取等方法,重点介绍线程的启动方法start()、守护线程的设置与判断以及线程休眠方法sleep()等

一、基本方法

方法说明
Thread.currentThread()静态方法。获取当前正在运行的线程
thread.getThreadGroup()获取线程所在线程组
thread.getName()获取线程的名字
thread.getPriority()获取线程的优先级。线程的优先级越高,抢占到cpu资源的机会越大
thread.setName(name)设置线程的名字
thread.setPriority(priority)设置线程优先级
thread.isAlive()判断线程是否还存活着
Thread.ActiveCount()获取当前程序中存活的线程数

代码示例:

new Thread(() -> {
    //通过Thread.currentThread()获取当前线程
    LOGGER.info("通过Thread.currentThread()获取当前线程");
    Thread currentThread = Thread.currentThread();
    LOGGER.info("当前线程:" + currentThread);
    LOGGER.info("当前线程-名字(thread.getName()):" + currentThread.getName());
    LOGGER.info("当前线程-优先级(thread.getPriority()):" + currentThread.getPriority());
    LOGGER.info("当前线程-线程组名字:" + currentThread.getThreadGroup().getName() + "\n");

    //通过thread.setName(name)设置线程名
    LOGGER.info("通过thread.setName(name)设置线程名");
    currentThread.setName("张三");
    //通过thread.setPriority(priority)设置优先级
    LOGGER.info("通过thread.setPriority(priority)设置优先级");
    currentThread.setPriority(Thread.MAX_PRIORITY);
    LOGGER.info("修改名字和优先级之后:" + currentThread);

    //通过thread.isAlive()判断当前线程是否还活着
    LOGGER.info("通过thread.isAlive()判断当前线程是否还活着:" + currentThread.isAlive() + "\n");
}).start();

执行结果:

13:16:32.941 [Thread-0] INFO com.wangst.study.bingfa.ThreadDemo - 通过Thread.currentThread()获取当前线程
13:16:32.944 [Thread-0] INFO com.wangst.study.bingfa.ThreadDemo - 当前线程:Thread[Thread-0,5,main]
13:16:32.944 [Thread-0] INFO com.wangst.study.bingfa.ThreadDemo - 当前线程-名字(thread.getName()):Thread-0
13:16:32.944 [Thread-0] INFO com.wangst.study.bingfa.ThreadDemo - 当前线程-优先级(thread.getPriority()):5
13:16:32.944 [Thread-0] INFO com.wangst.study.bingfa.ThreadDemo - 当前线程-线程组名字:main

13:16:32.944 [Thread-0] INFO com.wangst.study.bingfa.ThreadDemo - 通过thread.setName(name)设置线程名
13:16:32.944 [张三] INFO com.wangst.study.bingfa.ThreadDemo - 通过thread.setPriority(priority)设置优先级
13:16:32.944 [张三] INFO com.wangst.study.bingfa.ThreadDemo - 修改名字和优先级之后:Thread[张三,10,main]
13:16:32.944 [张三] INFO com.wangst.study.bingfa.ThreadDemo - 通过thread.isAlive()判断当前线程是否还活着:true

关于线程的字符串描述Thread[Thread-0,5,main],三个值分别代表:线程名、优先级、线程组名。

二、start()方法

start方法的作用就是将线程由NEW状态,变为RUNABLE状态。当线程创建成功时,线程处于NEW(新建)状态,如果你不调用start( )方法,那么线程永远处于NEW状态。调用start( )后,才会变为RUNABLE状态,线程才可以运行。

源码:

   /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

线程启动执行流程:

  • 检测线程状态,只有NEW状态下的线程才能继续,否则抛出异常(java.lang.IllegalThreadStateException 非法线程状态异常)
  • 被加入线程组
  • 调用start0()方法启动线程

线程组(ThreadGroup) :
可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织。用户创建的所有线程都属于指定线程组,如果没有显示指定属于哪个线程组,那么该线程就属于默认线程组(即main线程组)。默认情况下,子线程和父线程处于同一个线程组。只有在创建线程时才能指定其所在的线程组,线程运行中途不能改变它所属的线程组,也就是说线程一旦指定所在的线程组,就直到该线程结束。

三、守护线程的判断与设置

thread.isDaemon():判断线程是否是守护线程。
thread.setDaemon(true):将指定线程设置为守护线程。

守护线程:也可以称之为后台线程、非用户线程,即随系统结束而结束的线程。

更容易理解的说法:如果当前程序中,只剩下main线程和守护线程,且main线程执行完毕时,则系统结束。

与用户线程的区别:例如一个线程中通过while(true)无限等待。如果是用户线程,那么这个程序永远都不会关闭;如果是守护线程,那么当其他的用户线程执行完毕之后,程序及会关闭。

示例代码:

public static void main(String[] args) {
        logger.info("所谓[守护线程],可以理解为后台线程或非用户线程。");
        logger.info("当前程序中,只剩下main线程和守护线程,且main线程执行完毕时,系统结束。");
        Thread daemonThread = new Thread(() -> {
            try {
                while (true) {
                    Thread.sleep(1000);
                    logger.info("daemonThread线程正在工作...");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        logger.info("通过thread.isDaemon()判断当前线程[daemonThread]是否是守护线程:" + daemonThread.isDaemon());
        logger.info("通过thread.setDaemon(true)将指定线程设置为守护线程,随main线程结束而结束。");
        daemonThread.setDaemon(true);
        logger.info("通过thread.isDaemon()判断当前线程[daemonThread]是否是守护线程:" + daemonThread.isDaemon());
        daemonThread.start();
}

执行以上代码,该程序自然结束。如果将daemonThread.setDaemon(true); 注释掉,则该程序永远不会结束。

四、sleep()方法

sleep(long millis),又叫线程休眠方法,是一个静态方法。调用sleep方法后,线程进入TIMED_WAITING状态,并释放CPU资源,但不释放锁,到指定时间后,线程进入RUNNABLE状态。

Thread.sleep最终调用JVM_Sleep方法:

JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
  JVMWrapper("JVM_Sleep");

  if (millis < 0) {//参数校验
    THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
  }

  //如果线程已经中断,抛出中断异常,关于中断的实现,在另一篇文章中会讲解
  if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
    THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
  }
 //设置线程状态为SLEEPING
  JavaThreadSleepState jtss(thread);

  EventThreadSleep event;

  if (millis == 0) {
    //如果设置了ConvertSleepToYield(默认为true),和yield效果相同
    if (ConvertSleepToYield) {
      os::yield();
    } else {//否则调用os::sleep方法
      ThreadState old_state = thread->osthread()->get_state();
      thread->osthread()->set_state(SLEEPING);
      os::sleep(thread, MinSleepInterval, false);//sleep 1ms
      thread->osthread()->set_state(old_state);
    }
  } else {//参数大于0
   //保存初始状态,返回时恢复原状态
    ThreadState old_state = thread->osthread()->get_state();
    //osthread->thread status mapping:
    // NEW->NEW
    //RUNNABLE->RUNNABLE
    //BLOCKED_ON_MONITOR_ENTER->BLOCKED
    //IN_OBJECT_WAIT,PARKED->WAITING
    //SLEEPING,IN_OBJECT_WAIT_TIMED,PARKED_TIMED->TIMED_WAITING
    //TERMINATED->TERMINATED
    thread->osthread()->set_state(SLEEPING);
    //调用os::sleep方法,如果发生中断,抛出异常
    if (os::sleep(thread, millis, true) == OS_INTRPT) {
      if (!HAS_PENDING_EXCEPTION) {
        if (event.should_commit()) {
          event.set_time(millis);
          event.commit();
        }
        THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
      }
    }
    thread->osthread()->set_state(old_state);//恢复osThread状态
  }
  if (event.should_commit()) {
    event.set_time(millis);
    event.commit();
  }
JVM_END

os::sleep的源码如下:

int os::sleep(Thread* thread, jlong millis, bool interruptible) {
  assert(thread == Thread::current(),  "thread consistency check");
  //线程有如下几个成员变量:
  //ParkEvent * _ParkEvent ;          // for synchronized()
  //ParkEvent * _SleepEvent ;        // for Thread.sleep
  //ParkEvent * _MutexEvent ;      // for native internal Mutex/Monitor
  //ParkEvent * _MuxEvent ;         // for low-level muxAcquire-muxRelease
  ParkEvent * const slp = thread->_SleepEvent ;
  slp->reset() ;
  OrderAccess::fence() ;

//如果millis>0,传入interruptible=true,否则为false
  if (interruptible) {
    jlong prevtime = javaTimeNanos();

    for (;;) {
      if (os::is_interrupted(thread, true)) {//判断是否中断
        return OS_INTRPT;
      }

      jlong newtime = javaTimeNanos();//获取当前时间
      //如果linux不支持monotonic lock,有可能出现newtime<prevtime
      if (newtime - prevtime < 0) {
        assert(!Linux::supports_monotonic_clock(), "time moving backwards");
      } else {
        millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      }

      if(millis <= 0) {
        return OS_OK;
      }

      prevtime = newtime;
      {
        assert(thread->is_Java_thread(), "sanity check");
        JavaThread *jt = (JavaThread *) thread;
        ThreadBlockInVM tbivm(jt);
        OSThreadWaitState osts(jt->osthread(), false );

        jt->set_suspend_equivalent();
        slp->park(millis);
        jt->check_and_wait_while_suspended();
      }
    }
  } else {//如果interruptible=false
   //设置osthread的状态为CONDVAR_WAIT
    OSThreadWaitState osts(thread->osthread(), false );
    jlong prevtime = javaTimeNanos();

    for (;;) {
      jlong newtime = javaTimeNanos();

      if (newtime - prevtime < 0) {
        assert(!Linux::supports_monotonic_clock(), "time moving backwards");
      } else {
        millis -= (newtime - prevtime) / NANOSECS_PER_MILLISEC;
      }

      if(millis <= 0) break ;

      prevtime = newtime;
      slp->park(millis);//底层调用pthread_cond_timedwait实现
    }
    return OS_OK ;
  }
}

参考资料

https://blog.csdn.net/hanchao5272/article/details/79437370

https://www.cnblogs.com/aboutblank/p/3631453.html

https://blog.csdn.net/woniuhtl/article/details/95588673

https://www.jianshu.com/p/0964124ae822