Java线程(二)线程状态详解

线程状态

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为"运行"。 线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):表示线程阻塞于锁。
  4. 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
  5. 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
  6. 终止(TERMINATED):表示该线程已经执行完毕。

线程的状态图

  1. 初始状态(NEW) 实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态。尚未启动的线程的线程状态,也就是没有执行start方法。只是简单的创建了线程对象。

2.1. 就绪状态(RUNNABLE之READY) 就绪状态只是说你资格运行,调度程序没有挑选到你,你就永远是就绪状态。 调用线程的start()方法,此线程进入就绪状态。 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。 锁池里的线程拿到对象锁后,进入就绪状态。 2.2. 运行中状态(RUNNABLE之RUNNING) 线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一的一种方式。

  1. 阻塞状态(BLOCKED) 阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

  2. 等待(WAITING) 处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。

  3. 超时等待(TIMED_WAITING) 处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

  4. 终止状态(TERMINATED) 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它终止了。这个线程对象也许是活的,但是它已经不是一个单独执行的线程。线程一旦终止了,就不能复生。 在一个终止的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。

线程中的方法

  1. 'start()'方法: 用于启动线程。每个线程只能启动一次,多次启动线程报错: IllegalThreadStateException
java 复制代码
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running");
    }
}

public class StartExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 启动线程
    }
}
  1. 'run()'方法:线程的执行体
java 复制代码
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running");
    }
}

public class RunExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.run(); // 直接调用run方法,不会创建新线程
    }
}
  1. 'sleep(long millis)'方法:使线程休眠指定的时间。
java 复制代码
public class SleepExample {
    public static void main(String[] args) {
        System.out.println("Start of program");
        try {
            Thread.sleep(2000); // 休眠2秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("End of program after sleep");
    }
}
  1. 'yield'方法:建议线程让出CPU时间片。
java 复制代码
public class YieldExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 1 is running: " + i);
                Thread.yield(); // 建议让出CPU时间片
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread 2 is running: " + i);
            }
        });

        thread1.start();
        thread2.start();
    }
}
  1. 'join'方法:等待线程的结束。
java 复制代码
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running");
    }
}

public class JoinExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();

        try {
            thread.join(); // 等待thread线程结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Main thread finished");
    }
}
  1. 'interrupt'方法:中断线程
java 复制代码
public class InterruptExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Thread is running");
            }
        });

        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread.interrupt(); // 中断线程
    }
}
  1. 'isAlive()'方法:检查线程是否处于活动状态。
java 复制代码
class MyThread extends Thread {
    public void run() {
        System.out.println("Thread is running");
    }
}

public class IsAliveExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        System.out.println("Is thread alive? " + thread.isAlive()); // false
        thread.start();
        System.out.println("Is thread alive? " + thread.isAlive()); // true
    }
}
  1. 'isInterrupted()'方法:检查线程是否被中断
java 复制代码
public class IsInterruptedExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Thread is running");
            }
        });

        thread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        thread.interrupt(); // 中断线程
        System.out.println("Is thread interrupted? " + thread.isInterrupted()); // true
    }
}
  1. 'getName()' 方法:获取线程的名称。
java 复制代码
public class GetNameExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Thread name: " + Thread.currentThread().getName());
        });

        thread.start();
    }
}
  1. 'setName()'方法: 设置线程的名称
java 复制代码
public class SetNameExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("Thread name: " + Thread.currentThread().getName());
        });

        thread.setName("MyThread");
        thread.start();
    }
}
  1. 'getId()'方法: 获取线程的唯一标识
java 复制代码
public class GetIdExample {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println("Thread ID: " + thread.getId());
    }
}
  1. 'setPriority(int priority) 和 getPriority()' 方法:设置和获取线程的优先级。
java 复制代码
public class ThreadPriorityExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 priority: " + Thread.currentThread().getPriority());
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 priority: " + Thread.currentThread().getPriority());
        });

        thread1.setPriority(Thread.MIN_PRIORITY); // 设置最低优先级
        thread2.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级

        thread1.start();
        thread2.start();

Object 类中关于线程操作的方法

在Java中,'Object'类提供了一些与线程操作相关的方法,主要是用于线程同步和协作的。以下是'Object'类中与线程操作相关的方法:

  1. 'wait()': 导致当前线程等待,直到其他线程调用相同对象上的'notify()'或'notifyAll()'方法唤醒它。通常与'synchronized'关键字一起使用,用于实现线程之间的等待和通知机制。

  2. 'wait(long timeout)':与'wait()'类似,但可以指定最长等待时间,如果超过指定时间仍未被唤醒,则线程会自动苏醒。

  3. 'wait(long timeout, int nanos)':与'wait(long timeout)';类似,但允许指定纳秒级的等待时间。

  4. 'notify()':唤醒等待在同一对象上的单个线程,如果有多个线程在等待,只有其中一个会被唤醒。

  5. 'notifyAll()':唤醒等待在同一对象上的所有线程,允许他们竞争执行。

这些方法用在多线程环境下进行线程的等待和通知,以协调线程之间的执行顺序和互斥访问共享资源。通常,它们与'synchronized'块一起使用,以确保线程安全。

例如,以下是使用'wait()'和'notify()'来实现一个简单的生产者-消费者模型的示例:

java 复制代码
class SharedResource {
    private int data;
    private boolean available = false;

    public synchronized void produce(int value) {
        while (available) {
            try {
                wait(); // 等待消费者消费
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        data = value;
        available = true;
        System.out.println("Produced: " + value);
        notify(); // 唤醒消费者
    }

    public synchronized int consume() {
        while (!available) {
            try {
                wait(); // 等待生产者生产
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        available = false;
        System.out.println("Consumed: " + data);
        notify(); // 唤醒生产者
        return data;
    }
}

这里,'wait()'用于等待条件满足,'notify()'用于通知等待的线程,这个示例演示了如何使用Object类的线程操作方法来实现线程间的协作。

对于线程方法的详解

对于基本的 start、run、setName、getName就不再赘述了,本来也就是很简单的方法。

sleep是如何实现的

  • 'sleep'是一个静态方法,也定义在'Thread'类中,用于让当前线程休眠一段指定的时间。线程在休眠期间不会占用CPU,而是让其他线程有机会执行。

  • 'sleep'会导致线程进入阻塞状态,而且是一个固定的时间间隔,线程在休眠时间结束后会自动唤醒。

  • 'sleep'通常用于实现定时操作,时间间隔的等待和延迟执行。

  • 'sleep' 不会释放锁,如果线程在进入'synchronized'块或方法内部时持有锁,并调用了'sleep',则其他线程将无法获得该锁。 主要用于暂停线程的执行,但不释放已经获取的资源。

yield

  • 'yield'是一个静态方法,定义在'Thread'类中,用于提示线程调度器当前线程愿意让出CPU执行时间,使其他具有相同优先级的线程有机会执行。

  • 调用'yield'方法不会使线程进入等待或阻塞状态,它只是建议线程调度器在当前线程和其他线程之间进行切换。线程可能会再次立即执行,也可能需要等待一段时间。

  • 'yield'通常用于提高多线程程序的性能和公平性,但不能保证线程会立即切换到其他线程上。

  • 'yield'不会释放任何资源。它只是告诉线程调度器,当前线程愿意让出CPU时间片,以便其他具有相同或更高优先级的线程有机会执行。线程仍然保持其所有的资源,如锁或对象引用。

wait方法

  • 'wait'是一个实例方法,定义在'Object'类中,用于线程之间的协作和等待。线程可以调用'wait'方法等待其他线程的通知。

  • 'wait'会导致线程进入等待状态,线程会一直等待,直到其他线程调用相同对象上的'notify'或'notifyAll'方法来唤醒等待线程。

  • 'wait'通常用于线程之间的协作和通信,允许线程等待特定条件的发生。

  • 'wait'会释放线程持有的对象监视锁。当一个线程调用对象的'wait'方法时,它会释放该对象上的锁,并进入等待状态,允许其他线程进入该对象的'synchronized'块或方法。

join方法

join 方法是 Thread 类提供的一个重要方法,它的主要作用是等待调用 join 方法的线程(通常是主线程)等待被调用 join 方法的线程(通常是子线程)执行完毕。

具体来说,join 方法的作用如下:

  1. 等待线程执行完成 :调用 join 方法的线程会被阻塞,直到被调用 join 方法的线程执行完毕。这可以用于确保某个线程在另一个线程之后执行,通常用于主线程等待子线程执行完毕。
  2. 线程协作join 方法允许线程之间的协作,使得一个线程可以等待另一个线程完成后再继续执行。这在多线程编程中很有用,特别是需要等待某个线程的结果后再继续的情况。
  3. 异常传递 :如果被调用 join 方法的线程抛出了异常,调用 join 方法的线程可以捕获并处理该异常,从而可以进行适当的错误处理。
java 复制代码
public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Thread 1 - Count: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("Thread 2 - Count: " + i);
            }
        });

        thread1.start();
        thread2.start();

        thread1.join(); // 主线程等待thread1执行完毕
        thread2.join(); // 主线程等待thread2执行完毕

        System.out.println("Main thread finished");
    }
}

在上面的示例中,主线程启动了两个子线程 thread1thread2,然后分别调用了它们的 join 方法,这使得主线程等待 thread1thread2 执行完毕后再继续执行。这确保了线程的执行顺序和协作

线程优先级

在Java中,可以通过设置线程的优先级来影响线程的调度顺序,但线程优先级的效果在不同操作系统和JVM上可能会有差异,且通常不是一个可靠的方法来控制线程的执行顺序。线程优先级的设置可以作为一种提示,但不能保证绝对的执行顺序。

Java提供了线程的三个优先级常量:Thread.MIN_PRIORITYThread.NORM_PRIORITYThread.MAX_PRIORITY,分别对应最低、普通和最高的线程优先级。线程的优先级范围通常是1到10。你可以通过setPriority(int priority)方法来设置线程的优先级。

线程优先级的有效性取决于操作系统和JVM的实现,以及系统的负载情况。在一些情况下,线程的优先级设置可能会生效,但在其他情况下,操作系统可能会忽略线程的优先级设置,或者线程之间的优先级关系可能不如预期的那样。

因此,线程优先级通常不是一个可靠的控制线程执行顺序的方法,更可靠的方式是使用其他同步和协作机制,如锁、等待/通知机制、CountDownLatchCyclicBarrier等,来实现对线程的精确控制和协作

interrupt方法

线程优雅结束 调用Thread.interrupted()方法检查并清除线程的中断状态,并不会影响线程的可执行性。如果一个线程在中断状态下调用Thread.interrupted(),它仍然可以继续执行,只是在后续操作中需要根据中断状态来决定是否退出执行或采取其他行动。

线程的可执行性与中断状态是独立的。线程可以在检查并清除中断状态后继续执行,但需要根据中断状态来决定其行为。通常,线程在检查中断状态后会选择合适的行动,例如安全地终止自己或采取特定的处理方式。

例如,在下面的示例中,线程在中断状态下调用Thread.interrupted()来检查中断状态,然后根据中断状态决定是否退出执行:

java 复制代码
Thread workerThread = new Thread(() -> {
    while (true) {
        if (Thread.interrupted()) { // 检查并清除中断状态
            System.out.println("Thread interrupted. Cleaning up...");
            return; // 退出执行
        }
        // 执行一些工作
        System.out.println("Working...");
    }
});

workerThread.start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

workerThread.interrupt(); // 中断线程

try {
    workerThread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}

System.out.println("Main thread finished");

在上面的示例中,线程在中断状态下检查并清除中断状态,然后选择退出执行。线程仍然可以在之后的操作中继续执行,只是在检查中断状态后采取了退出的行动。线程的可执行性在这种情况下没有受到影响。

相关推荐
Victor3561 小时前
Redis(104)Redis的最大数据量是多少?
后端
Victor3561 小时前
Redis(105)Redis的数据类型支持哪些操作?
后端
鬼火儿8 小时前
SpringBoot】Spring Boot 项目的打包配置
java·后端
cr7xin8 小时前
缓存三大问题及解决方案
redis·后端·缓存
yoke菜籽8 小时前
面试150——字典树
面试·职场和发展
间彧9 小时前
Kubernetes的Pod与Docker Compose中的服务在概念上有何异同?
后端
间彧9 小时前
从开发到生产,如何将Docker Compose项目平滑迁移到Kubernetes?
后端
零雲9 小时前
java面试:有了解过RocketMq架构么?详细讲解一下
java·面试·java-rocketmq
间彧9 小时前
如何结合CI/CD流水线自动选择正确的Docker Compose配置?
后端
间彧9 小时前
在多环境(开发、测试、生产)下,如何管理不同的Docker Compose配置?
后端