Java多线程3-Thread简介

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/f59130/article/details/81089318

一、线程生命周期

线程的状态有NEW、RUNNABLE、RUNNING、BLOCKED和TERMINATED五个状态。如下图所示:
Thread生命周期

  • NEW:当我们new一个Thread对象后,在没有执行start方法之前,这仅仅是一个普通的Java对象。可以说线程处于NEW状态。
  • RUNNABLE:当执行start方法后,JVM会创建一个线程并进入RUNNABLE状态,等待操作系统的调度。
  • RUNNING:当操作系统调度处于RUNNABLE状态的线程成功后,此时该线程处于执行状态,并执行其逻辑代码。如果调用stop方法则进入TERMINATED状态;调用sleep/wait、获取锁资源或进行某个IO阻塞会进入BLOCKED状态;CPU时间片用完或者调用yield方法(放弃CPU执行),则会进入RUNNABLE状态。
  • BLOCKED:阻塞状态。
  • TERMINATED:终止状态。

二、start()和run()方法的区别

如下,我们新建一个线程,并执行start()方法,使其进入RUNNABLE状态。然而我们重写的run()方法却执行了,这是为什么?

public static void main(String[] args) {
    Thread t1 = new Thread() {
       @Override
        public void run() {
            System.out.println("hello world.");
        }
    };
    t1.start();
}

我们看下start()的源码:

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 */
            }
        }
    }

可以看到,start()方法的核心为:start0()方法。start0()为native方法。在线程执行时,start0()会调用run()方法。

结论1:启动线程,应该执行start()方法。如果调用run()方法,有时会看到相同的执行结果,但实际上只是执行了Thread对象的一个普通方法,并没有多线程运行。

结论2:从start()的源码可以看到,如果执行两次start(),会出现IllegalThreadStateException

结论3:如果一个线程的逻辑单元执行完,会进入TERMINATED状态,再次执行start()也是不行的。通过上一节的状态转换图看到,从TERMINATED状态并不能回到RUNNABLE状态。

三、sleep()和yield()方法的区别

  • yield():调用该方法会通知CPU调度器放弃CPU资源,线程从RUNINIG状态转到RUNNABLE状态。会导致线程上下文切换。但如果CPU资源不紧张,调度器会忽略yield请求。
  • sleep():调用该方法会使得当前线程暂停指定的时间,线程从RUNNING状态转到BLOCKED状态。但不会释放锁资源。

四、join()方法

我们先从一个例子说起:

public class ThreadDemo {

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("t1 thread");
            }
        };

        Thread t2 = new Thread() {
            @Override
            public void run() {
                try {
                    t1.join();
                } catch (Exception e) {}
                System.out.println("t2 thread");
            }
        };

        t1.start();
        t2.start();
        t2.join();
        System.out.println("main thread");
    }
}
t1 thread
t2 thread
main thread

无论输出多次其结果都是固定的t1→t2→main。如果去掉join()方法,则三者的顺序则不固定。

当前线程Main执行中,join线程t2,则Main线程进入BLOCKED状态,直到t2结束生命周期或达到指定时间。同样地,t2线程执行中,join线程t1,则t2线程会等到t1线程执行完才会执行。

结论:当前执行线程join某个线程之后,该执行线程会等待join线程执行完/达到指定时间才会继续进行执行。常见的场景是一个任务分割成多个小任务,等各个小任务均执行完成后,再执行汇总操作。

猜你喜欢

转载自blog.csdn.net/f59130/article/details/81089318