22.jdk源码阅读之Thread(上)

1. 写在前面

Java 中的 Thread 类是多线程编程的基础,也是我们日常工作中用的比较多的类,但是你真的了解它吗?下面这几个问题你是否有思考过?

  1. start() 和 run() 方法有什么区别?
  2. 什么是线程的生命周期?
  3. 什么是守护线程(Daemon Thread)?
  4. 如何中断线程?
  5. 什么是线程优先级?
  6. 什么是线程组(ThreadGroup)?
  7. 什么是线程本地变量(ThreadLocal)?
  8. 什么是死锁?如何避免

2. 全局视角

在这里插入图片描述

2.1 Runnable 接口

  • 定义了一个方法 run(),需要实现该接口的类提供具体的任务执行逻辑。
  • Thread 类通过实现 Runnable 接口,使得线程对象可以直接执行任务。
public interface Runnable {
    public abstract void run();
}

2.2 Thread 类的部分重要方法

  • start():启动线程,调用 run() 方法。
  • run():线程执行的任务逻辑,通常需要重写。
  • join():等待线程终止。
  • sleep(long millis):使当前线程休眠指定的毫秒数。
  • interrupt():中断线程。
  • isInterrupted():检查线程是否被中断。
  • setDaemon(boolean on):将线程设置为守护线程
  • getPriority() 和 setPriority(int newPriority):获取和设置线程的优先级。
  • getName() 和 setName(String name):获取和设置线程的名称。
  • getState():获取线程的状态。

2.3 Thread 类的构造方法

在这里插入图片描述

Thread 类提供了多种构造方法,以便灵活创建线程对象。常见的构造方法包括:

  • Thread()
  • Thread(Runnable target)
  • Thread(Runnable target, String name)
  • Thread(String name)
  • Thread(ThreadGroup group, Runnable target)
  • Thread(ThreadGroup group, Runnable target, String name)

下面我们来详细介绍下各个构造方法是如何使用的。

3. 从使用说起

3.1 无参构造方法

无参构造方法创建一个新的线程对象,但不指定任何任务或线程名称。

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("Thread running using no-arg constructor");
            }
        };
        thread.start();
    }
}

3.2 使用 Runnable 作为参数的构造方法

这个构造方法接受一个实现了 Runnable 接口的对象作为参数,用于指定线程要执行的任务。

public class Main {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread running using Runnable constructor");
            }
        };
        Thread thread = new Thread(task);
        thread.start();
    }
}

3.3 使用 Runnable 和 String 作为参数的构造方法

这个构造方法接受一个实现了 Runnable 接口的对象和一个线程名称,用于指定线程要执行的任务和线程的名称。

public class Main {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread running using Runnable and String constructor");
            }
        };
        Thread thread = new Thread(task, "MyThread");
        thread.start();
        System.out.println("Thread Name: " + thread.getName());
    }
}

3.4 使用 String 作为参数的构造方法

这个构造方法接受一个线程名称,用于指定线程的名称。

public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread("MyThread") {
            @Override
            public void run() {
                System.out.println("Thread running using String constructor");
            }
        };
        thread.start();
        System.out.println("Thread Name: " + thread.getName());
    }
}

3.5 使用 ThreadGroup 和 Runnable 作为参数的构造方法

这个构造方法接受一个线程组和一个实现了 Runnable 接口的对象,用于指定线程所属的线程组和要执行的任务。

public class Main {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("MyGroup");
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread running using ThreadGroup and Runnable constructor");
            }
        };
        Thread thread = new Thread(group, task);
        thread.start();
        System.out.println("Thread Group: " + thread.getThreadGroup().getName());
    }
}

3.6 使用 ThreadGroup, Runnable, 和 String 作为参数的构造方法

这个构造方法接受一个线程组、一个实现了 Runnable 接口的对象和一个线程名称,用于指定线程所属的线程组、要执行的任务和线程的名称。

public class Main {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("MyGroup");
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread running using ThreadGroup, Runnable, and String constructor");
            }
        };
        Thread thread = new Thread(group, task, "MyThread");
        thread.start();
        System.out.println("Thread Group: " + thread.getThreadGroup().getName());
        System.out.println("Thread Name: " + thread.getName());
    }
}

3.7 使用 ThreadGroup, Runnable, String, 和 long 作为参数的构造方法

这个构造方法接受一个线程组、一个实现了 Runnable 接口的对象、一个线程名称和一个栈大小,用于指定线程所属的线程组、要执行的任务、线程的名称和线程的栈大小。

public class Main {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("MyGroup");
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("Thread running using ThreadGroup, Runnable, String, and long constructor");
            }
        };
        Thread thread = new Thread(group, task, "MyThread", 1024);
        thread.start();
        System.out.println("Thread Group: " + thread.getThreadGroup().getName());
        System.out.println("Thread Name: " + thread.getName());
    }
}

4. start()方法的底层源码

上面的使用中我们发现start()是用的比较多的一个方法,下面我们就深入看看它的源码实现,源码如下:

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

start() 方法用于启动一个新线程,使其调用 run() 方法并在新的线程中执行。这个方法是线程启动的核心部分,确保线程正确地从 “NEW” 状态转变到 “RUNNABLE” 状态。下面是对这个方法的逐步解析:

4.1 方法签名

public synchronized void start() {
  • public:表示这个方法是公有的,可以从类的外部调用。
  • synchronized:表示这个方法是同步的,确保同一时间只有一个线程可以执行该方法,防止并发问题。

4.2 方法实现

    /**
     * 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".
     */

这段注释说明了 start() 方法的一些背景信息:

  • 这个方法不会被主线程或由虚拟机创建的 “系统” 线程调用。
  • 任何将来添加到这个方法的新功能也可能需要添加到虚拟机中。
  • threadStatus 为 0 表示线程处于 “NEW” 状态。
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
  • 这段代码检查线程的状态。如果 threadStatus 不是 0(即线程不是处于 “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);
  • 这段代码通知线程组(ThreadGroup)该线程即将启动。线程组会将该线程添加到其线程列表中,并减少未启动线程的计数。
    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 */
        }
    }
  • boolean started = false;:初始化一个布尔变量 started 为 false,用于跟踪线程是否成功启动。
  • start0();:调用本地方法 start0(),这是一个本地方法,用于启动新线程。start0() 是一个私有的本地方法,由 JVM 实现,用来执行底层的线程启动操作。
  • started = true;:如果 start0() 成功执行,将 started 设置为 true。

4.3 finally 块

  • finally 块确保在任何情况下都能执行清理操作。
  • 如果 start0() 方法抛出异常且 started 仍然为 false,则调用 group.threadStartFailed(this); 通知线程组线程启动失败。
  • catch (Throwable ignore):捕获所有异常,并忽略它们。这是为了确保如果 start0() 抛出异常,它会被传递到调用栈上,而不会被这个捕获块阻止。

5. run()方法

/**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

run()方法的源代码如上,看起来很简单,run() 方法是线程执行的入口点。当一个线程启动后,JVM 会调用该线程的 run() 方法。这个方法的默认实现检查线程是否有一个关联的 Runnable 对象,如果有,则调用该 Runnable 对象的 run() 方法。

5.1 方法签名和注释

/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see     #start()
* @see     #stop()
* @see     #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
  • 如果该线程是用一个单独的 Runnable 对象构造的,那么会调用这个 Runnable 对象的 run() 方法;否则,这个方法什么也不做,直接返回。
  • Thread 类的子类应该重写这个方法,以提供具体的执行逻辑。
  • 提供了一些相关方法的链接,包括 start()、stop() 和 Thread(ThreadGroup, Runnable, String)。

5.2 方法实现

    if (target != null) {
        target.run();
    }
}
  • if (target != null)

    • 检查 target 是否为 null。target 是一个 Runnable 对象,表示线程要执行的任务。
    • target 是 Thread 类的一个实例变量,当使用 Thread(Runnable target) 构造方法创建线程时,会将传入的 Runnable 对象赋值给 target。
  • target.run()

    • 如果 target 不为 null,则调用 target 的 run() 方法。这意味着如果线程是用一个 Runnable 对象创建的,那么实际执行的是这个 Runnable 对象的 run() 方法
    • 如果 target 为 null,则什么也不做,直接返回

5.3 例子

下面是一个使用 Thread 类和 Runnable 接口的示例,展示了如何使用 run() 方法:

5.3.1 使用 Runnable 接口

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable run method executed.");
    }
}

public class Main {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start(); // This will call myRunnable's run() method
    }
}

在这个例子中,MyRunnable 实现了 Runnable 接口,并重写了 run() 方法。创建 Thread 对象时,将 MyRunnable 对象传递给 Thread 的构造方法。调用 thread.start() 时,最终会调用 MyRunnable 对象的 run() 方法。

5.3.2 继承 Thread 类

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread run method executed.");
    }
}

public class Main {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start(); // This will call MyThread's run() method
    }
}

在这个例子中,MyThread 继承了 Thread 类,并重写了 run() 方法。创建 MyThread 对象并调用 myThread.start() 时,会执行 MyThread 类的 run() 方法。

系列文章

1.JDK源码阅读之环境搭建

2.JDK源码阅读之目录介绍

3.jdk源码阅读之ArrayList(上)

4.jdk源码阅读之ArrayList(下)

5.jdk源码阅读之HashMap

6.jdk源码阅读之HashMap(下)

7.jdk源码阅读之ConcurrentHashMap(上)

8.jdk源码阅读之ConcurrentHashMap(下)

9.jdk源码阅读之ThreadLocal

10.jdk源码阅读之ReentrantLock

11.jdk源码阅读之CountDownLatch

12.jdk源码阅读之CyclicBarrier

13.jdk源码阅读之Semaphore

14.jdk源码阅读之线程池(上)

15.jdk源码阅读之线程池(下)

16.jdk源码阅读之ArrayBlockingQueue

17.jdk源码阅读之LinkedBlockingQueue

18.jdk源码阅读之CopyOnWriteArrayList

19.jdk源码阅读之FutureTask

20.jdk源码阅读之CompletableFuture

21.jdk源码阅读之AtomicLong

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

至真源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值