生命周期
根据 jdk 官方文档,线程状态有以下几种
NEW 尚未启动的线程处于此状态。
RUNNABLE 在 Java 虚拟机中执行的线程处于此状态。
BLOCKED 被阻塞等待监视器锁定的线程处于此状态。
WAITING 正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED 已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
如图示所见
代码演示
NEW / TIMED_WAITING / TERMINATED
 package ThreadMethod;
import java.util.concurrent.TimeUnit;
public class ThreadState {    public static void main(String[] args) throws InterruptedException {        Thread thread = new Thread(new Runnable() {            @Override            public void run() {                try {                    TimeUnit.SECONDS.sleep(3);                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }                
            }
        });        System.out.println("线程状态:"+ thread.getState());        thread.start();        Thread.sleep(2000);        System.out.println("线程状态:" + thread.getState());        Thread.sleep(2000);        System.out.println("线程状态:" + thread.getState());
    }
}
       复制代码
 
结果
 线程状态:NEW线程状态:TIMED_WAITING线程状态:TERMINATED线程状态:TERMINATED
       复制代码
 
WAITING / BLOCKED
 package ThreadMethod;
public class BlockThreadState implements Runnable {
    static final Object lock = new Object();    public static void main(String[] args) throws InterruptedException {
        BlockThreadState blockThread = new BlockThreadState();        Thread thread = new Thread(blockThread);        System.out.println("thread state : " + thread.getState());        thread.start();        Thread.sleep(20);        System.out.println("thread state : " + thread.getState());        synchronized (lock){            lock.notify();        }        System.out.println("thread state : " + thread.getState());        Thread.sleep(20);        System.out.println("thread state : " + thread.getState());
    }
    @Override    public void run() {        try {            synchronized (lock){				// wait状态                lock.wait();                // synchronized重新拿到锁 处于block状态                for (int i = 0; i < 1000000000; i++) {                    continue;                }            }        } catch (InterruptedException e) {            e.printStackTrace();        }    }}
       复制代码
 
结果
 thread state : NEWthread state : WAITINGthread state : BLOCKEDthread state : TERMINATED
       复制代码
 常见方法
wait()
wait()方法执行后,会阻塞线程,同时释放锁,如果想要唤醒该线程,则需要以下条件
另一个线程调用这个对象的 notify()方法,且刚好被唤醒的时本线程
另一个线程调用了这个对象的 notifyAll()方法
过了 wait(long timeout) 规定的超时时间,如果传入 0 就是永久等待
线程自身调用了 interrupt()
PS: 使用 wait()方法时,必须先拥有 monitor 锁, 也就是说 wait 方法需要放在同步代码块中执行
notify/notifyAll
notify/notifyAll 用于唤醒线程,当另一个线程调用 wait()进入 waitting 状态时,另一个线程调用 notifyAll()可唤醒当前线程(如果有多个线程,使用 notify 并不一定能够唤醒线程)
组合使用示例
 public class BlockThread {    public static void main(String[] args) {        Message message = new Message();
        // 创建一个等待线程        Thread waitThread = new Thread(new WaitThread(message));        // 创建一个唤醒线程        Thread notifyThread = new Thread(new NotifyThread(message));
        // 启动等待线程和唤醒线程        waitThread.start();        notifyThread.start();    }}
// 共享的消息类class Message {    private boolean isReady = false;
    // 等待方法    public synchronized void waitForMessage() {        while (!isReady) {            try {                // 当消息不可用时,线程进入等待状态                System.out.println("线程进入等待状态");                wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }        // 执行到这里表示收到消息,进行处理        System.out.println("收到消息!");    }
    // 唤醒方法    public synchronized void sendMessage() {        // 做一些准备工作,例如获取消息
        // 唤醒等待的线程        isReady = true;        System.out.println("唤醒等待的线程");        notify();    }}
// 等待线程class WaitThread implements Runnable {    private Message message;
    public WaitThread(Message message) {        this.message = message;    }
    @Override    public void run() {        // 等待消息        message.waitForMessage();    }}
// 唤醒线程class NotifyThread implements Runnable {    private Message message;
    public NotifyThread(Message message) {        this.message = message;    }
    @Override    public void run() {        // 发送消息        message.sendMessage();    }}
/*
线程进入等待状态唤醒等待的线程收到消息!
Process finished with exit code 0*/
       复制代码
 图示 monitor 锁
wait、notify 实现生产者消费者模式
 import java.util.LinkedList;import java.util.List;import java.util.TreeMap;
public class ProductAndConsumer {
    List<Object> container = new LinkedList<>();
    public static void main(String[] args) {        ProductAndConsumer productAndConsumer = new ProductAndConsumer();        Thread thread1 = new Thread(new Product(productAndConsumer));        Thread thread2 = new Thread(new Consumer(productAndConsumer));        thread1.start();        thread2.start();    }
    public synchronized void addObject() throws InterruptedException {        if (container.size() >=100){            wait();        }        container.add(new Object());        System.out.println("正在生产第"+ container.size() + "个");        notify();    }
    public synchronized void conObject() throws InterruptedException {        if (container.size() == 0){            wait();        }        container.remove(0);        System.out.println("正在消费第"+ container.size() + "个");        notify();    }}
class Product implements Runnable{    private final ProductAndConsumer productAndConsumer;
    Product(ProductAndConsumer productAndConsumer){        this.productAndConsumer = productAndConsumer;    }
    @Override    public void run() {       while (true){           try {               productAndConsumer.addObject();           } catch (InterruptedException e) {               e.printStackTrace();           }       }    }}
class Consumer implements Runnable{    private final ProductAndConsumer productAndConsumer;
    Consumer(ProductAndConsumer productAndConsumer){        this.productAndConsumer = productAndConsumer;    }
    @Override    public void run() {       while (true){           try {               productAndConsumer.conObject();           } catch (InterruptedException e) {               e.printStackTrace();           }       }    }}
       复制代码
 解释
图示
sleep()
Thread.sleep()是 Java 中一个静态 native 方法,用于使当前线程进入休眠状态(暂停执行)一段指定的时间。
它的方法签名为:
 public static native void sleep(long millis) throws InterruptedException;
       复制代码
 参数 millis 表示线程休眠的时间,以毫秒为单位。传入的值是一个正整数,表示线程要休眠的毫秒数。注意,该方法会抛出 InterruptedException 异常,因为线程在休眠期间可能被其他线程中断。
特点和用法
线程阻塞:调用 Thread.sleep()方法会导致当前线程暂停执行,进入阻塞状态。在指定的时间内,线程不会进行任何操作。
时间精度:传入的休眠时间是以毫秒为单位,但实际的休眠时间可能会稍长或稍短。具体的精度取决于底层操作系统和 JVM 的实现。
中断响应:如果在线程休眠期间,另一个线程中断了正在休眠的线程,Thread.sleep()方法会抛出 InterruptedException 异常。可以在 catch 块中处理该异常,或者将异常继续向上抛出。
不会释放锁:Thread.sleep()方法会暂停当前线程的执行,但不会释放任何锁。如果线程在执行同步代码块或同步方法时调用了 Thread.sleep(),其他线程仍无法获得该锁。
静态方法:Thread.sleep()是一个静态方法,可以直接通过 Thread 类调用,无需创建线程对象。
用途:常见的用途包括模拟延迟、定时任务、控制线程执行顺序等。
Thread.sleep 和 TimeUnit 比较
精度和可读性: Thread.sleep()的参数是以毫秒为单位的时间值,表示线程要休眠的时间。而 TimeUnit 提供了更高层次的时间单位,如 TimeUnit.SECONDS 表示秒,TimeUnit.MILLISECONDS 表示毫秒等。使用 TimeUnit 可以使代码更具可读性,而不需要手动计算毫秒数。
异常处理: Thread.sleep()方法会抛出 InterruptedException 异常,因为线程在休眠期间可能会被其他线程中断。而 TimeUnit 方式不会直接抛出异常,需要开发者手动处理中断情况。
静态与非静态: Thread.sleep()是 Thread 类的静态方法,可以直接通过类名调用。而 TimeUnit 是一个枚举类,需要通过具体的枚举常量来调用其方法,例如 TimeUnit.SECONDS.sleep(1)。
可读性和易用性: 使用 TimeUnit 可以提高代码的可读性,因为可以直观地表示时间单位。此外,TimeUnit 还提供了其他方法,如 TimeUnit.toMillis()、TimeUnit.toSeconds()等,方便进行时间单位之间的转换。
TimeUnit 源码
以下是截取部分源码
 public enum TimeUnit {      /**     * Performs a {@link Thread#sleep(long, int) Thread.sleep} using     * this time unit.     * This is a convenience method that converts time arguments into the     * form required by the {@code Thread.sleep} method.     *     * @param timeout the minimum time to sleep. If less than     * or equal to zero, do not sleep at all.     * @throws InterruptedException if interrupted while sleeping     */    public void sleep(long timeout) throws InterruptedException {        if (timeout > 0) {            long ms = toMillis(timeout);            int ns = excessNanos(timeout, ms);            Thread.sleep(ms, ns);        }    }}
       复制代码
 可以看到 TimeUnit 底层还是调用了 Thread.sleep() 有一个比较隐含的地方就是当使用过 TimeUnit 的 sleep 方法时,如果传入的时间小于是不会进入 if 判断,而 Thread.sleep()方法如果传参小于 0 则会抛出异常(源码见下)
 public static void sleep(long millis, int nanos)throws InterruptedException {    if (millis < 0) {        throw new IllegalArgumentException("timeout value is negative");    }
    if (nanos < 0 || nanos > 999999) {        throw new IllegalArgumentException(                            "nanosecond timeout value out of range");    }
    if (nanos >= 500000 || (nanos != 0 && millis == 0)) {        millis++;    }
    sleep(millis);}
       复制代码
 join()
Thread.join()是 Java 中的一个方法,用于等待调用该方法的线程执行完毕。它的作用是让当前线程等待指定线程执行结束,然后再继续执行当前线程的后续代码。
简单来说就是阻塞主线程。
简单示例
 package ThreadMethod;
public class JoinMethod {    public static void main(String[] args) {        Thread thread1 =  new Thread(()->{            try {                Thread.sleep(3000);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("子线程执行完毕");        },"子线程");        thread1.start();        try {            thread1.join();        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println("主线程执行完毕");    }}
       复制代码
 
结果
以看到子线程等待了 3 秒,但是最终还是子线程先执行完毕在执行主线程打印,原因是因为 thread1.join 对主线程进行了阻塞,这是主线程需要等子线程执行完毕才会执行后面的语句
特点和注意事项
等待执行: 调用 join()方法的线程将会等待指定线程执行完毕。如果指定线程已经执行完毕,则 join()方法会立即返回。
阻塞调用线程: 在调用 join()方法期间,当前线程将会被阻塞,暂停执行。只有当指定线程执行完毕后,当前线程才会解除阻塞,继续执行。
异常处理: join()方法会抛出 InterruptedException 异常,因为在等待过程中,当前线程可能会被中断。可以在 catch 块中处理该异常,或将异常继续向上抛出。
顺序执行: 通过使用 join()方法,可以控制线程的执行顺序。调用 join()方法后,当前线程会等待指定线程执行完毕,然后再继续执行后续代码。
调用对象: join()方法是一个实例方法,需要通过线程对象调用。例如,如果 thread1 是一个 Thread 对象,可以使用 thread1.join()来等待 thread1 执行完毕。
源码和底层实现
以下截取部分 Thread.join 源码
 // 入口public final void join() throws InterruptedException {    join(0);}
// 调用的join(0)public final synchronized void join(long millis)    throws InterruptedException {        long base = System.currentTimeMillis();        long now = 0;
        if (millis < 0) {            throw new IllegalArgumentException("timeout value is negative");        }
        if (millis == 0) {            while (isAlive()) {                wait(0);            }        } else {            while (isAlive()) {                long delay = millis - now;                if (delay <= 0) {                    break;                }                wait(delay);                now = System.currentTimeMillis() - base;            }        }    }
// 实际调用的本地native方法  waitpublic final native void wait(long timeout) throws InterruptedException;
       复制代码
 可以看到 join 的底层还是使用 wait 方法实现的,子线程调用 wait 方法让主线程进入等待状态,在运行结束后自动调用 notify 方法唤醒主线程。(具体唤醒的方法在 jvm 中)
有一点疑问我没有找到解释:为什么调用子线程的 wait(0)方法,阻塞的确是主线程呢?
CountDownLatch 和 CyclicBarrier
使用 countDownLatch 和 CyclicBarrier 也可以实现线程之间的阻塞,具体暂不讨论
yeild() (让步)
yield() 是一个静态方法,它属于 Thread 类,用于提示调度器将当前线程让出 CPU 的执行权,使得其他具有相同优先级的线程有机会执行。
yield() 方法的调用并不能保证一定会使其他线程获得执行机会,它仅是一个提示。具体的调度行为取决于操作系统和 JVM 的实现。因此,在实际应用中,不应过度依赖 yield() 方法来控制线程的执行顺序,而应使用更可靠的线程同步机制来实现需要的线程协作和同步。
评论