精华内容
下载资源
问答
  • 使用wait和notify

    2020-12-28 23:03:43
    小结 wait和notify用于多线程协调运行: 在synchronized内部可以调用wait()使线程进入等待状态; 必须在已获得的锁对象上调用wait()方法; 在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程; ...

    在Java程序中,synchronized解决了多线程竞争的问题。例如,对于一个任务管理器,多个线程同时往队列中添加任务,可以用synchronized加锁:

    class TaskQueue {

    Queue queue = new LinkedList<>();

    public synchronized void addTask(String s) {

    this.queue.add(s);

    }

    }

    但是synchronized并没有解决多线程协调的问题。

    仍然以上面的TaskQueue为例,我们再编写一个getTask()方法取出队列的第一个任务:

    class TaskQueue {

    Queue queue = new LinkedList<>();

    public synchronized void addTask(String s) {

    this.queue.add(s);

    }

    public synchronized String getTask() {

    while (queue.isEmpty()) {

    }

    return queue.remove();

    }

    }

    上述代码看上去没有问题:getTask()内部先判断队列是否为空,如果为空,就循环等待,直到另一个线程往队列中放入了一个任务,while()循环退出,就可以返回队列的元素了。

    但实际上while()循环永远不会退出。因为线程在执行while()循环时,已经在getTask()入口获取了this锁,其他线程根本无法调用addTask(),因为addTask()执行条件也是获取this锁。

    因此,执行上述代码,线程会在getTask()中因为死循环而100%占用CPU资源。

    如果深入思考一下,我们想要的执行效果是:

    线程1可以调用addTask()不断往队列中添加任务;

    线程2可以调用getTask()从队列中获取任务。如果队列为空,则getTask()应该等待,直到队列中至少有一个任务时再返回。

    因此,多线程协调运行的原则就是:当条件不满足时,线程进入等待状态;当条件满足时,线程被唤醒,继续执行任务。

    对于上述TaskQueue,我们先改造getTask()方法,在条件不满足时,线程进入等待状态:

    public synchronized String getTask() {

    while (queue.isEmpty()) {

    this.wait();

    }

    return queue.remove();

    }

    当一个线程执行到getTask()方法内部的while循环时,它必定已经获取到了this锁,此时,线程执行while条件判断,如果条件成立(队列为空),线程将执行this.wait(),进入等待状态。

    这里的关键是:wait()方法必须在当前获取的锁对象上调用,这里获取的是this锁,因此调用this.wait()。

    调用wait()方法后,线程进入等待状态,wait()方法不会返回,直到将来某个时刻,线程从等待状态被其他线程唤醒后,wait()方法才会返回,然后,继续执行下一条语句。

    有些仔细的童鞋会指出:即使线程在getTask()内部等待,其他线程如果拿不到this锁,照样无法执行addTask(),肿么办?

    这个问题的关键就在于wait()方法的执行机制非常复杂。首先,它不是一个普通的Java方法,而是定义在Object类的一个native方法,也就是由JVM的C代码实现的。其次,必须在synchronized块中才能调用wait()方法,因为wait()方法调用时,会释放线程获得的锁,wait()方法返回后,线程又会重新试图获得锁。

    因此,只能在锁对象上调用wait()方法。因为在getTask()中,我们获得了this锁,因此,只能在this对象上调用wait()方法:

    public synchronized String getTask() {

    while (queue.isEmpty()) {

    // 释放this锁:

    this.wait();

    // 重新获取this锁

    }

    return queue.remove();

    }

    当一个线程在this.wait()等待时,它就会释放this锁,从而使得其他线程能够在addTask()方法获得this锁。

    现在我们面临第二个问题:如何让等待的线程被重新唤醒,然后从wait()方法返回?答案是在相同的锁对象上调用notify()方法。我们修改addTask()如下:

    public synchronized void addTask(String s) {

    this.queue.add(s);

    this.notify(); // 唤醒在this锁等待的线程

    }

    注意到在往队列中添加了任务后,线程立刻对this锁对象调用notify()方法,这个方法会唤醒一个正在this锁等待的线程(就是在getTask()中位于this.wait()的线程),从而使得等待线程从this.wait()方法返回。

    我们来看一个完整的例子:

    import java.util.*;

    ----

    public class Main {

    public static void main(String[] args) throws InterruptedException {

    var q = new TaskQueue();

    var ts = new ArrayList();

    for (int i=0; i<5; i++) {

    var t = new Thread() {

    public void run() {

    // 执行task:

    while (true) {

    try {

    String s = q.getTask();

    System.out.println("execute task: " + s);

    } catch (InterruptedException e) {

    return;

    }

    }

    }

    };

    t.start();

    ts.add(t);

    }

    var add = new Thread(() -> {

    for (int i=0; i<10; i++) {

    // 放入task:

    String s = "t-" + Math.random();

    System.out.println("add task: " + s);

    q.addTask(s);

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

    }

    });

    add.start();

    add.join();

    Thread.sleep(100);

    for (var t : ts) {

    t.interrupt();

    }

    }

    }

    class TaskQueue {

    Queue queue = new LinkedList<>();

    public synchronized void addTask(String s) {

    this.queue.add(s);

    this.notifyAll();

    }

    public synchronized String getTask() throws InterruptedException {

    while (queue.isEmpty()) {

    this.wait();

    }

    return queue.remove();

    }

    }

    这个例子中,我们重点关注addTask()方法,内部调用了this.notifyAll()而不是this.notify(),使用notifyAll()将唤醒所有当前正在this锁等待的线程,而notify()只会唤醒其中一个(具体哪个依赖操作系统,有一定的随机性)。这是因为可能有多个线程正在getTask()方法内部的wait()中等待,使用notifyAll()将一次性全部唤醒。通常来说,notifyAll()更安全。有些时候,如果我们的代码逻辑考虑不周,用notify()会导致只唤醒了一个线程,而其他线程可能永远等待下去醒不过来了。

    但是,注意到wait()方法返回时需要重新获得this锁。假设当前有3个线程被唤醒,唤醒后,首先要等待执行addTask()的线程结束此方法后,才能释放this锁,随后,这3个线程中只能有一个获取到this锁,剩下两个将继续等待。

    再注意到我们在while()循环中调用wait(),而不是if语句:

    public synchronized String getTask() throws InterruptedException {

    if (queue.isEmpty()) {

    this.wait();

    }

    return queue.remove();

    }

    这种写法实际上是错误的,因为线程被唤醒时,需要再次获取this锁。多个线程被唤醒后,只有一个线程能获取this锁,此刻,该线程执行queue.remove()可以获取到队列的元素,然而,剩下的线程如果获取this锁后执行queue.remove(),此刻队列可能已经没有任何元素了,所以,要始终在while循环中wait(),并且每次被唤醒后拿到this锁就必须再次判断:

    while (queue.isEmpty()) {

    this.wait();

    }

    所以,正确编写多线程代码是非常困难的,需要仔细考虑的条件非常多,任何一个地方考虑不周,都会导致多线程运行时不正常。

    小结

    wait和notify用于多线程协调运行:

    在synchronized内部可以调用wait()使线程进入等待状态;

    必须在已获得的锁对象上调用wait()方法;

    在synchronized内部可以调用notify()或notifyAll()唤醒其他等待线程;

    必须在已获得的锁对象上调用notify()或notifyAll()方法;

    已唤醒的线程还需要重新获得锁后才能继续执行。

    展开全文
  • wait和notify使用场景

    2020-12-20 10:27:42
    1.wait和notify方法必须要在同步块或者方法里面且成对出现使用 2.先waitnotify才ok 传统的synchronizedLock实现等待唤醒通知的约束 线程先要获得并持有锁,必须在锁块(synchronized或Lock)中 必须要先等待后唤醒...

    1.wait和notify方法必须要在同步块或者方法里面且成对出现使用
    2.先wait后notify才ok

    传统的synchronized和Lock实现等待唤醒通知的约束
    线程先要获得并持有锁,必须在锁块(synchronized或Lock)中
    必须要先等待后唤醒,线程才能够被唤醒

    LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
    LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法,归根结底,LockSupport调用Unsafe中的native代码

    LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每个线程都有一个Permit(许可)
    permit只有两个值1和0,默认是0
    可以把许可看成是一种(0,1)信号量(Seamphore),但与Seamphore不同的是,许可的累加上限是1.

    为什么可以先唤醒线程后阻塞线程?
    因为unpark获得了一个凭证,之后再调取park方法,就可以名正言顺的凭证消费,故不会阻塞。

    形象的理解
    线程阻塞需要消耗凭证(permit),这个凭证最多只有1个。
    当调用park方法时
    如果有凭证,则会直接消耗掉这个凭证然后正常退出;
    如果无凭证,就必须阻塞等待凭证可以用
    而unpark则相反,它会增加一个凭证,但凭证最多只能有1个,累加无效

    为什么唤醒两次后阻塞两次,但最终结果还是会阻塞线程?
    因为凭证的数量最多为1,连续调用两次unpark和调用一次unpark一样,只会增加一个凭证,而调用两次park却需要消费两个凭证,证不够,不能放行。

    展开全文
  • wait和notify多线程是需要同步协作,比如QuartzSchedulerThread,作为Quartz的任务调度线程,如果设置该线程暂停,那么这个线程就必须wait, 等外界通知继续的时候,再执行任务。// check if we're supposed to pause...

    wait和notify

    多线程是需要同步协作,比如QuartzSchedulerThread,作为Quartz的任务调度线程,如果设置该线程暂停,那么这个线程就必须wait, 等外界通知继续的时候,再执行任务。

    // check if we're supposed to pause...

    synchronized (sigLock) {

    while (paused && !halted.get()) {

    try {

    // wait until togglePause(false) is called...

    //使当前线程暂停1秒

    sigLock.wait(1000L);

    } catch (InterruptedException ignore) {

    }

    }

    if (halted.get()) {

    break;

    }

    }

    如果要调用对象的wait方法,就必须先获得这个对象的锁–>synchronized (sigLock)。

    当线程执行了wait后,就释放了锁,这样当线程在wait期间,QuartzScheduler就有机会获得锁。然后就可以执行唤醒操作notify。

    如果多个线程都在wait sigLock, 当调用 notify的时候只能唤醒其中一个线程(由JVM决定),调用notifyAll则能唤醒全部线程。

    从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。

    相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。

    QuartzScheduler执行start方法,会调用schedThread.togglePause(false); 取消QuartzSchedulerThread的暂停,初始化的时候QuartzSchedulerThread的pause是true

    void togglePause(boolean pause) {

    synchronized (sigLock) {

    paused = pause;

    if (paused) {

    signalSchedulingChange(0);

    } else {

    sigLock.notifyAll();

    }

    }

    }

    sleep 和 wait 的区别

    Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制。

    经典的ABC打印问题

    建立三个线程,A线程打印10次A,B线程打印10次B, C线程打印10次C,要求线程同时运行,交替打印10次ABC。

    public class MyThreadPrinter2 implements Runnable {

    private String name;

    private Object prev;

    private Object self;

    private MyThreadPrinter2(String name, Object prev, Object self) {

    this.name = name;

    this.prev = prev;

    this.self = self;

    }

    @Override

    public void run() {

    int count = 10;

    while (count > 0) {

    synchronized (prev) {

    synchronized (self) {

    System.out.print(name);

    count--;

    self.notify();

    }

    try {

    prev.wait();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    public static void main(String[] args) throws Exception {

    Object a = new Object();

    Object b = new Object();

    Object c = new Object();

    MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);

    MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);

    MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);

    new Thread(pa).start();

    new Thread(pb).start();

    new Thread(pc).start(); }

    }

    A—>B—>C—>A

    要想控制线程执行的顺序,就需要使用锁机制,每个线程都持有自己的锁,并且在自己上一个线程的锁释放的时候才能执行。

    比如B执行的条件:

    1. A 锁释放

    2. 获得自己的B 锁

    A执行的条件:

    1. C锁释放

    2. 获得自己的A 锁

    执行流程:

    1. 获得前一个线程的锁 synchronized (prev)

    2. 获得自己的锁,执行方法,释放自己的锁,通知下一个线程 synchronized (self) { self.notify(); }

    3. 释放前一个线程的锁,继续监听锁,prev.wait();

    总结:

    wait就是监听锁对象,将当前线程放入锁的等待队列中。

    notify就是通知监听线程,告诉监听该锁的线程,你们可以获得锁了。

    notify只会让其中一个监听线程执行,由JVM决定。notifyAll会通知所有的监听线程。

    那么问题来了!

    如果A 执行了System.out.print(name);方法后,CPU又切换到了主线程。启动了B 和C 线程,

    B 获取不到A锁,继续等待。C 获得了B锁,然后准备获得自己的C锁,这时A居然执行完成了,释放了C锁,所以C就可以执行方法了。 然后就是落后的B,先获得A锁,再获得B锁,执行。

    就成了ACBACB…..

    模拟代码:

    public void run() {

    int count = 10;

    while (count > 0) {

    synchronized (prev) {

    synchronized (self) {

    System.out.print(name);

    count--;

    try{

    //先睡会,CPU你去找其他线程

    Thread.sleep(1);

    }

    catch (InterruptedException e){

    e.printStackTrace();

    }

    self.notify();

    }

    try {

    prev.wait();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    解决:

    public class MyThreadPrinter2 implements Runnable {

    private String name;

    private Object prev;

    private Object self;

    private MyThreadPrinter2(String name, Object prev, Object self) {

    this.name = name;

    this.prev = prev;

    this.self = self;

    }

    @Override

    public void run() {

    int count = 10;

    while (count > 0) {

    synchronized (prev) {

    synchronized (self) {

    System.out.print(name);

    count--;

    try{

    Thread.sleep(1);

    }

    catch (InterruptedException e){

    e.printStackTrace();

    }

    self.notify();

    }

    try {

    prev.wait();

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    }

    }

    }

    public static void main(String[] args) throws Exception {

    Object a = new Object();

    Object b = new Object();

    Object c = new Object();

    MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);

    MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);

    MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);

    new Thread(pa).start();

    //使主线程睡眠,这样CPU就不会在A执行结束前去执行B或者C,给A足够的时间完成调度。

    Thread.sleep(10);

    //主线程睡眠之间到了时候,恢复CPU调度,启动线程B

    new Thread(pb).start();

    Thread.sleep(10);

    new Thread(pc).start();

    Thread.sleep(10);

    }

    }

    synchronized的4种用法

    1.方法声明

    public synchronized void synMethod() {

    //方法体

    }

    一次只能一个线程进入该方法。

    2.对某一代码块使用

    public int synMethod(int a1){

    synchronized(a1) {

    //一次只能有一个线程进入

    }

    }

    文章来自

    展开全文
  • ReentrantLock可以配合Condition来实现wait和notify的await()signalAll()来实现线程的等待唤醒。 示例代码: public class TestClass { private final Lock lock = new ReentrantLock(); private final ...

    ReentrantLock 可以配合Condition来实现wait和notify的await()和signalAll()来实现线程的等待和唤醒。

    示例代码:

    public class TestClass {
        private final Lock lock = new ReentrantLock();
        private final Condition condition = lock.newCondition();
        private int count;
    
        public void add() {
            try {
                lock.lock();
                try {
                    for (int i = 0; i < 5; i++) {
                        count ++;
                        System.out.println("Test ReentrantLock "+Thread.currentThread()+" running");
                        System.out.println("Test ReentrantLock  "+count);
                        if(count == 2){
                            condition.await();
                        }
    
                    }
    
                } finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    public class MyClass {
    
        public static void main(String[] args) {
            final TestClass testClass = new TestClass();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    testClass.add();
                }
            }).start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
    
                    testClass.add();
                }
            }).start();
        }
        
    }

    运行结果:

     可以看到,Thread-0在输入2后停止了运行并由Thread-1继续运行下去

    展开全文
  • 只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()notifyAll()方法。 如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。 还有一个原因是为了避免wait和notify之间产生竞...
  • Condition 是 JDK 1.5 中提供的用来替代 wait notify 的线程通讯方法,那么一定会有人问:为什么不能用 wait notify 了? 哥们我用的好好的。老弟别着急,听我给你细说...之所以推荐使用 Condition 而非 ...
  • wait和notify

    2021-02-28 17:19:10
    面试题: 实现一个容器,提供两个方法,add,size写两个线程,线程1...这里使用wait和notifywait会释放锁,而notify不会释放锁 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以 no.
  • wait和notify方法详解

    2021-04-01 09:45:42
    wait和notify方法并不是Thread特有的方法,而是Object中的方法,也就是说在JDK中的每一个类都拥有这两个方法,那么这两个方法到底有什么神奇之处可以使线程阻塞又可以唤醒线程呢?我们先来说说wait方法,下面是wait...
  • wait notify

    多人点赞 2021-11-25 15:46:54
    由于线程之间是抢占式执行的,因此线程之间执行的先后顺序难以预知. ...wait( )方法:当操作条件不成熟,就等待 notify( )方法:当条件成熟时,通知指定的线程来工作 wait( )方法 notify( )方法 notifyAll( )方法 ...
  • wait(): Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.notify(): Wakes up a single thread that is waiting on this object's...
  • 而while循环中有一个同步代码块,它的锁对象为object对象,同时又要求wait和notify方法必须在同步代码块中,且共有一个锁对象,否则报错。 通过这个实例发现*等待唤醒机制中, 在Thread thread=new Thread(myRunable...
  • public class WaitNotify { static Object lock = new Object(); static boolean flag = false; public static void main(String[] args) { new Thread(new WaitThread(), "WaitThread").start(); try { Thread....
  • 本文讲解Java中wait()、notify(),通过一个标准的使用实例,来讨论下这两个方法的作用使用时注意点,这两个方法被提取到顶级父类Object对象中,地位等同于toString()方法。 一、wait()和notify()含义 wait()...
  • 1. wait方法 当条件不成熟时就等待 运行分为三步: 1.释放锁 2.等到通知 3.收到通知后尝试重新获取锁...wait和notify结合: import java.util.Scanner; public class ThreadDemo18 { public static void main(Strin
  • 为什么 java wait/notify 必须与 synchronized 一起使用 这个问题就是书本上没怎么讲解,就是告诉我们这样处理,但没有解释为什么这么处理?我也是基于这样的困惑去了解原因。 synchronized wait/notify 这两个是...
  • 一、wait()和notify()作用 wait()和notify()方法是Object类的方法,因为Object类是所有类的根类,因此所有类都有这两个方法。 当调用对象的wait()方法时会释放获取的该对象的锁。 既然是释放锁,我们首先要有锁才能...
  • 1. 为什么需要wait/notify方法 由于条件不满足,小南不能继续进行计算 但小南如果一直占用着锁,其它人就得一直阻塞,效率太低 于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,...
  • 1.释义:wait()方法将当前线程暂停,置于“预执行队列”中,而notify()则用于通知一个在wait等待中的线程,可以继续执行了2.wait()和notify()的使用条件:wait()和notify()必须置于同步方法同步代...
  • wait()和notify()是直接隶属于Object类,也就是说,所有对象都拥有这一对方法。初看起来这十分 不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 ...
  • 6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException at java.lang.Object.notify(Native Method) at multiplyThread.run(multiplyThread.java:49) java.lang.IllegalMonitorStateException ...
  • JUC-wait和notify

    2021-09-27 22:54:09
    Object类中的wait和notify方法实现线程等待唤醒 1、正常情况 public class WaitNotifyDemo { static Object lock = new Object(); public static void main(String[] args) { new Thread(()->{ ...
  • 其实要实现这样的方式有很多,今天我主要给大家介绍的是怎么使用wait和notify实现这样一个案例。简单介绍wait() - 方法wait()的作用是使当前执行代码的线程进行等待,它是Object类的方法,该方法用来将当前线程置入...
  • Object的wait方法调用后线程会放弃对象锁,只有该对象调用notify/notifyAll方法才进入对象wait后面的进行运行状态 // wait() // notify() // notifyAll(); // 通过调用某对象的wait()方法能让当前线程阻塞 // 通过...
  • Java并发编程-wait和notify原理剖析

    千次阅读 2021-02-05 15:07:55
    1.小故事 - 为什么需要 wait 有一对小孩都要使用算盘CPU进行计算,为了计算过程中的计算安全,老王设计了一个synchronized锁,让每个时刻只有一个线程进入房间成为owner的主人。这样只能排队一个一个线程的来,从而...
  • 文章目录 前言 一、特点 二、wait()方法的作用 三、notify()方法的作用 四、wait()和notify()的使用 重点 前言 关于Object类中的wait()和notify()方法 (生产者消费者模式) 一、特点 wait()和notify()方法并不是...
  • synchronized是Java中常用的锁机制,synchronized+Object.wait/notify是常用的等待唤醒机制,那它们的实现原理是什么呢?本文就synchronize...
  • 文章目录1:等待(wait)通知(notify)的介绍2:等待(wait)通知(notify)的执行机制 1:等待(wait)通知(notify)的介绍 JDK提供了两个非常重要的接口线程:等待wait()方法通知notify()方法。这两个方法...
  • 关于wait()暂停的是持有锁的对象,所以想调用wait()必须为:对象.wait();notify()唤醒的是等待锁的对象,调用:对象.notify();如下:Object obj = newObject();...}注意:wait(),notify(),notifyA...
  • As I understand, I am suppose to call wait() on the mutex, when I want the current thread to stop working until another thread calls notify() on the same mutex object. That doesn't seem to be working....
  • 为什么wait必须在synchronized保护的同步代码种使用? 首先我们来看看wait方法的源码注释是怎么写的? //wait method should always be used in a loop: synchronized (ojb) { while(condition does not hold) ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 162,047
精华内容 64,818
关键字:

wait和notify的区别