3.1等待/通知机制(wait/notify)

本文详细介绍了Java中线程间的同步机制,重点讲解了wait和notify方法的使用及注意事项,包括它们如何帮助实现线程间的等待和唤醒机制。

wait/notify

要点wait()
执行前在调用wait()之前,必须先要获得对象锁,即只有在同步方法或者同步代码块中调用wait()方法。
执行作用wait()使进程进入等待(阻塞状态),在收到通知或者被中断之前都会进入预执行队列。
执行之后1执行wait()之后,当前线程释放改对象锁,在通知前与其他线程重新竞争资源
执行之后2执行wait()之后,如果没有使用notify()通知其他线程,即使该对象资源空闲,其他使用过wait()线程由于没有通知会继续阻塞直到这个对象发出notify或者notifyAll
/wait()是Object对象的方法。

要点notify()
执行前在调用notify()之前,必须先要获得对象锁,即只有在同步方法或者同步代码块中调用notify()方法。
执行作用通知一个等待该对象资源的线程进入就绪状态,如果有多个线程等待,怎有线程规划器来挑选一个进入就绪状态(区别notify和notifyAll
执行之后执行notify之后,当前线程并不会立即释放锁,只有当执行完sychronized方法或者代码块的时候,当前线程才会释放锁
/notify()是Object对象的方法。

wait()之后锁立即释放和notify()之后锁不立即释放

针对上面的锁释放不释放,来做一个小的例子

先做wait()之后锁立即释放

package com.myObject;

public class Object1 {

    public void mothd1(Object lock) {
        synchronized (lock) {
            try {
                System.out.println("wait begin");
                lock.wait();
                System.out.println("wait end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
package com.myThread;

import com.myObject.Object1;

public class Thread1a extends Thread {
    Object lock;
    Object1 object1 = new Object1();

    public Thread1a(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        object1.mothd1(lock);
    }
}
package com.myThread;

import com.myObject.Object1;

public class Thread1b extends Thread {
    Object lock;
    Object1 object1 = new Object1();

    public Thread1b(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        object1.mothd1(lock);
    }
}
package com.test;

import com.myThread.Thread1a;
import com.myThread.Thread1b;

public class Test1 {
    public static void main(String[] args){
        Object lock = new Object();
        Thread1a thread1a = new Thread1a(lock);
        thread1a.start();
        Thread1b thread1b = new Thread1b(lock);
        thread1b.start();

    }
}

打印结果

wait begin
wait begin

下面是验证notify()之后锁不立即释放

package com.myObject;

public class Object1 {

    public void mothd1(Object lock) {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName()+" wait begin " + System.currentTimeMillis());
                lock.wait();
                System.out.println(Thread.currentThread().getName()+" wait end " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public void mothd2(Object lock) {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName()+"notify begin " + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);//延时用于测试是否有立即释放锁
                System.out.println(Thread.currentThread().getName()+"notify end " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
package com.myThread;

import com.myObject.Object1;

public class Thread1a extends Thread {
    Object lock;
    Object1 object1 = new Object1();

    public Thread1a(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        object1.mothd1(lock);
    }
}
package com.myThread;

import com.myObject.Object1;

public class Thread1b extends Thread {
    Object lock;
    Object1 object1 = new Object1();

    public Thread1b(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        object1.mothd2(lock);
    }
}
package com.test;

import com.myThread.Thread1a;
import com.myThread.Thread1b;

public class Test1 {
    public static void main(String[] args) throws InterruptedException{
        Object lock = new Object();
        Thread1a thread1a = new Thread1a(lock);
        thread1a.setName("A");
        thread1a.start();
        Thread.sleep(1000);
        Thread1b thread1b = new Thread1b(lock);
        thread1b.setName("B");
        thread1b.start();

    }
}

打印结果

A wait begin 1453090470087
Bnotify begin 1453090471088
Bnotify end 1453090476088
A wait end 1453090476088

说明notify必须在执行完synchronized同步方法或者同步代码块之后才释放锁

wait之后使用interrupt

package com.myObject;

public class Object2 {

    public void mothd1(Object lock) {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName()+" wait begin " + System.currentTimeMillis());
                lock.wait();
                System.out.println(Thread.currentThread().getName()+" wait end " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }


}
package com.myThread;

import com.myObject.Object2;

public class Thread2 extends Thread {
    Object lock;
    Object2 object2 = new Object2();

    public Thread2(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        object2.mothd1(lock);
    }
}

package com.test;

import com.myThread.Thread2;

public class Test2 {
    public static void main(String[] args) throws InterruptedException{
        Object lock = new Object();
        Thread2 thread2 = new Thread2(lock);
        thread2.setName("A");
        thread2.start();
        Thread.sleep(1000);
        thread2.interrupt();

    }
}

当线程处于wait()时,调用线程对象的interrupt()会出现InterruptedException

总结:释放锁的时刻有
1)同步代码完成
2)抛出异常或者return
3)wait()方法

### 3.1 condition_variable wait 是否可能导致死锁 是的,`std::condition_variable` 的 `wait()` 函数在使用不当的情况下确实可能导致死锁。尽管 `wait()` 本身不会直接引发死锁,但其使用依赖于互斥锁(`std::unique_lock<std::mutex>`)和共享状态的正确管理,若逻辑设计不合理或资源释放顺序错误,就可能造成线程永远阻塞,从而形成死锁或类死锁状态。 例如,若某个线程在调用 `wait()` 之前未能正确获取锁,或者在等待期间其他线程无法获取锁来修改条件变量所依赖的状态,就会导致该线程无法被唤醒。此外,若通知线程在调用 `notify_one()` 或 `notify_all()` 之前未能修改共享状态,也可能导致等待线程无法退出等待状态,从而形成逻辑死锁。 更严重的情况是,若 `condition_variable` 对象在其等待线程尚未被唤醒时被销毁,可能导致调用 `notify_one()` 或 `notify_all()` 的线程在尝试唤醒时访问已释放的资源,从而引发未定义行为,甚至卡死在 `notify_one()` 调用中。这种情况在资源管理不当或对象生命周期控制不当时尤为常见[^3]。 为避免这些问题,应确保: - 所有对共享状态的访问都在互斥锁保护下进行; - 在调用 `notify_one()` 或 `notify_all()` 前,确保条件变量所依赖的状态已正确更新; - 避免在等待线程尚未被唤醒前销毁 `condition_variable` 或其相关资源; - 使用带谓词的 `wait()` 以防止虚假唤醒导致逻辑错误。 以下是一个典型错误示例,展示了因对象生命周期管理不当导致的死锁风险: ```cpp std::mutex mtx; std::condition_variable cv; bool ready = false; struct Task { void wait() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return ready; }); } }; void thread_func(Task* task) { task->wait(); // 若 task 已被释放,则访问非法内存 } void notify() { std::lock_guard<std::mutex> lock(mtx); ready = true; cv.notify_all(); } int main() { Task* task = new Task(); std::thread t(thread_func, task); delete task; // 提前释放 task notify(); t.join(); } ``` 在上述代码中,`thread_func` 中的线程可能在 `wait()` 过程中访问已被释放的 `task` 对象,导致未定义行为。若 `notify()` 调用发生在 `wait()` 之前,则线程可能永远阻塞[^3]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值