JavaEE 多线程第四节 (线程核心操作----线程开始/线程终止)

时间:2024-10-28 08:25:37

目录

1. start() 和 run() :

总结:

2. 线程的终止控制

例子

2. lambda表达式中的变量捕获

例子

3. 为什么需要“有效final”

总结


1. start()run()

  • start() 方法
    • 负责通知 JVM 和操作系统创建新线程。
    • 新线程被调度时,会调用 run() 方法,线程的任务会在新线程中运行。
    • 只能调用一次,重复调用会抛出 IllegalThreadStateException 异常。
Thread t = new Thread(() -> {
    System.out.println("Thread is running");
});

// 正确的用法,启动新线程
t.start();  

// 错误的用法,重新启动线程会抛出 IllegalThreadStateException
t.start();  // 抛出异常
  • run() 方法
    • 定义线程的实际执行内容,但不会创建新线程。
    • 如果直接调用 run(),它只是在当前线程中执行代码,而不创建新的执行线程
Thread t = new Thread(() -> {
    System.out.println("Thread is running");
});

// 错误:这只是普通的方法调用,没有创建新线程
t.run();  // 直接在主线程执行,没有创建新的线程

总结:

  • start() 是启动线程的正确方法,它调用 run() 来执行任务,并通过底层系统 API 创建真正的操作系统级线程。
  • 不能在同一个线程对象上调用 start() 两次,否则会抛出 IllegalThreadStateException
  • run() 方法可以被直接调用,但它不会启动新线程,而是在当前线程执行任务。

2. 线程的终止控制

在Java中,主线程和子线程之间的协作是重要的。比如在一个子线程中运行一个无限循环,当某个条件满足时终止这个循环。常用的控制方式是通过一个共享变量,让主线程更新这个变量,然后子线程检查变量值来决定是否退出。

例子
public class Demo01 {
    private static boolean isQuit = false;

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("子线程结束");
        });
        
        t.start();
        
        // 主线程等待2秒后,将isQuit置为true
        Thread.sleep(2000);
        isQuit = true;
        System.out.println("主线程设置isQuit=true,通知子线程结束");
    }
}

在这个例子中,isQuit变量被主线程和子线程共享。主线程在2秒后将isQuit置为true,从而通知子线程结束循环。这种方法简单直接,但在多线程环境中需要确保isQuit变量的可见性(即使用volatile关键字或其他同步手段)。

2. lambda表达式中的变量捕获

在Java中,lambda表达式中引用的外部变量必须是**"有效final"**的(即变量的值在lambda表达式内部不能更改)。这个限制主要是因为lambda表达式会将变量“捕获”到内部类的作用域中去,而该变量必须是不可变的。

例子

在以下代码中,如果将共享变量isQuit直接放入lambda表达式中,必须确保其不可变或使用final修饰。

public class Demo02 {
    private static boolean isQuit = false;

    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (!isQuit) {
                System.out.println("hello");
            }
        });
        
        t.start();
    }
}

如果在lambda表达式中修改了isQuit的值,则会报错,因为Java不允许在lambda中修改捕获的非final变量。

3. 为什么需要“有效final”

在Java的并发编程中,lambda表达式和匿名类具有闭包特性,这意味着它们可以引用外部作用域的变量,但变量必须是不可变的。这背后的原因包括以下几点:

  • 线程安全:不可变的变量在并发情况下是线程安全的。
  • 内存模型:Java的内存模型对线程间的变量可见性有严格要求,不可变变量可以保证不会被其他线程修改,从而简化了JVM的实现。

总结

  1. 使用isQuit控制线程的退出,主线程可以在某个时间点设置isQuittrue,从而通知子线程退出。
  2. lambda表达式中捕获外部变量时需要保证变量是不可变的,即有效final
  3. 使用volatile可以保证多线程环境中变量的可见性