线程阻塞、等待、挂起概念讲解

  1. 线程阻塞 (Blocking)

    • 发生在当线程尝试执行某个操作(如I/O操作、等待锁等)而无法继续,此时线程会放弃CPU控制权。
    • 可分为主动阻塞(如调用sleep()join())和被动阻塞(如等待锁)。
    • 线程阻塞期间不消耗CPU资源,等待特定条件满足后,由操作系统重新调度执行。
  2. 线程等待 (Waiting)

    • 特指在Java中使用同步机制(如wait()方法)时,线程自愿放弃对象的监视器锁并进入等待状态。
    • 等待的线程需要被其他线程通过notify()notifyAll()唤醒,之后才能重新尝试获取锁并继续执行。
    • 与阻塞类似,等待期间也不占用CPU资源,但强调线程间通过监视器进行的协调。
  3. 线程挂起 (Suspending)

    • 在现代编程实践中,“挂起”一词较少使用,且其含义可能随上下文而变化。
    • 传统意义上,挂起意味着暂停线程的执行,通常在调试时使用,但现在已不鼓励直接挂起线程,因为可能导致死锁等问题。
    • 与阻塞和等待相比,挂起更多关联于开发者手动控制线程生命周期,而不是程序逻辑自然流程的一部分。

解释:

监视器的工作原理

每个Java对象在内存中除了存储其实际数据外,还包含一个对象头,这个对象头中有一部分被称为Mark Word,它包含了对象的锁状态等信息。当一个线程试图通过synchronized关键字进入一个同步代码块或方法时,实际上是在尝试获取这个对象监视器的所有权。具体过程大致如下:

  1. 尝试获取锁:线程执行到synchronized代码块或方法时,会尝试获取对象监视器锁。如果锁未被其他线程持有,当前线程就会成功获取锁,并继续执行。

  2. 进入等待队列:如果锁已经被其他线程持有,那么当前线程会被阻塞,并被放置到这个对象监视器相关的入口集(Entry Set)或等待集(Wait Set)中。入口集存放的是正在尝试获取锁的线程,而等待集存放的是调用了wait()方法等待被唤醒的线程。

  3. 释放锁:持有锁的线程执行完同步代码块或方法,或者因异常退出时,会自动释放锁。此时,如果入口集中有等待的线程,JVM会选择一个(通常是按照一定规则,如FIFO)来给予锁并唤醒它,让其继续执行。

监视器的作用

  1. 锁的状态管理:监视器确实与对象头中的锁状态信息紧密相关。对象头中记录的Mark Word可以包含锁标志位,指示对象是否被锁定,以及锁的类型(轻量级、偏向、重量级)。这是监视器机制的基础,用来识别和管理锁的占有状态。

  2. 持有锁的线程标识:在轻量级锁和偏向锁的模式下,对象头中的Mark Word也可能间接或直接包含持有锁的线程ID(通过线程的栈帧指针)。这意味着监视器通过对象头信息来快速识别哪个线程拥有锁,尤其是轻量级锁通过CAS操作直接在对象头中交换线程ID。

  3. 同步和线程调度:监视器更重要的作用在于提供一种同步机制,包括管理入口集和等待集。尽管这些集合本身不存储在对象头中,但监视器概念涵盖了对这些集合的维护和操作,比如决定哪些线程可以进入同步块(即入口集的管理),以及如何处理调用wait()后线程的挂起和唤醒(等待集的管理)。

例子:

public class Buffer {
    private int product = 0; // 仓库中的产品数量
    private final int capacity = 10; // 仓库容量

    // 生产产品的方法
    public synchronized void produce() throws InterruptedException {
        while (product == capacity) { // 如果仓库满,生产者等待
            wait(); // 生产者线程等待
        }
        product++; // 生产一个产品
        System.out.println("Produced: " + product);
        notifyAll(); // 通知所有等待的线程(消费者或另一个生产者)
    }

    // 消费产品的方法
    public synchronized void consume() throws InterruptedException {
        while (product == 0) { // 如果仓库空,消费者等待
            wait(); // 消费者线程等待
        }
        product--; // 消费一个产品
        System.out.println("Consumed: " + product);
        notifyAll(); // 通知所有等待的线程(生产者或其他消费者)
    }
}

public class Producer implements Runnable {
    private Buffer buffer;

    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 20; i++) {
                buffer.produce();
                Thread.sleep(100); // 模拟生产时间
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

public class Consumer implements Runnable {
    private Buffer buffer;

    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        try {
            for (int i = 0; i < 20; i++) {
                buffer.consume();
                Thread.sleep(150); // 模拟消费时间
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Buffer buffer = new Buffer();

        Thread producerThread = new Thread(new Producer(buffer), "Producer");
        Thread consumerThread1 = new Thread(new Consumer(buffer), "Consumer 1");
        Thread consumerThread2 = new Thread(new Consumer(buffer), "Consumer 2");

        producerThread.start();
        consumerThread1.start();
        consumerThread2.start();
    }
}

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Addison_Wang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值