JAVA多线程之wait/notify

概述

本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法。html

目录以下:java

  • ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用
  • ②wait() 与 notify/notifyAll() 的执行过程
  • ③中断 调用wait()方法进入等待队列的 线程
  • ④notify 通知的顺序不能错
  • ⑤多线程中测试某个条件的变化用 if 仍是用 while?

①wait() 与 notify/notifyAll 方法必须在同步代码块中使用

wait() 与 notify/notifyAll() 是Object类的方法,在执行两个方法时,要先得到锁。那么怎么得到锁呢?编程

JAVA多线程之Synchronized关键字--对象锁的特色。文章中介绍了使用synchronized关键字得到锁。所以,wait() 与 notify/notifyAll() 常常与synchronized搭配使用,即在synchronized修饰的同步代码块或方法里面调用wait() 与 notify/notifyAll()方法。多线程

②wait() 与 notify/notifyAll() 的执行过程

因为 wait() 与 notify/notifyAll() 是放在同步代码块中的,所以线程在执行它们时,确定是进入了临界区中的,即该线程确定是得到了锁的。ide

当线程执行wait()时,会把当前的锁释放,而后让出CPU,进入等待状态。学习

当执行notify/notifyAll方法时,会唤醒一个处于等待该 对象锁 的线程,而后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁。测试

从这里能够看出,notify/notifyAll()执行后,并不当即释放锁,而是要等到执行完临界区中代码后,再释放。故,在实际编程中,咱们应该尽可能在线程调用notify/notifyAll()后,当即退出临界区。即不要在notify/notifyAll()后面再写一些耗时的代码。示例以下:this

public class Service {

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

    public void synNotifyMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
                lock.notify();
                Thread.sleep(5000);
                System.out.println("  end notify() ThreadName="
                        + Thread.currentThread().getName() + " time="
                        + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在第3行的testMethod()中调用 wait(),在第17行的synNotifyMethod()中调用notify()线程

从上面的代码能够看出,wait() 与 notify/notifyAll()都是放在同步代码块中才可以执行的。若是在执行wait() 与 notify/notifyAll() 以前没有得到相应的对象锁,就会抛出:java.lang.IllegalMonitorStateException异常。code

在第8行,当ThreadA线程执行lock.wait();这条语句时,释放得到的对象锁lock,并放弃CPU,进入等待队列。

当另外一个线程执行第23行lock.notify();,会唤醒ThreadA,可是此时它并不当即释放锁,接下来它睡眠了5秒钟(sleep()是不释放锁的,事实上sleep()也能够不在同步代码块中调用),直到第28行,退出synchronized修饰的临界区时,才会把锁释放。这时,ThreadA就有机会得到另外一个线程释放的锁,并从等待的地方起(第9行)起开始执行。

接下来是两个线程类,线程类ThreadA调用testMethod()方法执行lock.wait();时被挂起,另外一个线程类synNotifyMethodThread调用synNotifyMethod()负责唤醒挂起的线程。代码以下:

public class ThreadA extends Thread {
    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}

public class SynNotifyMethodThread extends Thread {
    private Object lock;

    public SynNotifyMethodThread(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.synNotifyMethod(lock);
    }
}

再接下来是测试类:

public class Test {

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

        Object lock = new Object();

        ThreadA a = new ThreadA(lock);
        a.start();

        //NotifyThread notifyThread = new NotifyThread(lock);
       // notifyThread.start();

        SynNotifyMethodThread c = new SynNotifyMethodThread(lock);
        c.start();
    }
}

③中断 调用wait()方法进入等待队列的 线程

示例代码以下:

public class Service {

    public void testMethod(Object lock) {
        try {
            synchronized (lock) {
                System.out.println("begin wait()");
                lock.wait();
                System.out.println("  end wait()");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("出现异常");
        }
    }
}

public class ThreadA extends Thread {

    private Object lock;

    public ThreadA(Object lock) {
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        Service service = new Service();
        service.testMethod(lock);
    }
}

注意,在第23行wait()方法是Object类的对象lock调用的。而下面的interrupt()方法是ThreadA类的对象调用的。在ThreadA里面,将Object的对象做为参数传给了testMethod()方法,ThreadA的run()方法去调用testMethod(),从而wait()使ThreadA的线程暂停了(暂停当前执行wait()的线程)。从这里能够看出一个区别:

Object类中与线程有关的方法:

1)notify/notifyAll

2)wait()/wait(long)

java.lang.Thread中与之相关的方法:

1)interrupt()

2)sleep()/sleep(long)

3)join()/suspend()/resume()....

测试类代码以下:

public class Test {

    public static void main(String[] args) {

        try {
            Object lock = new Object();

            ThreadA a = new ThreadA(lock);
            a.start();

            Thread.sleep(5000);

            a.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

当执行第13行的interrupt()时,处于wait中的线程“当即”被唤醒(通常是当即响应中断请求),并抛出异常。此时,线程也就结束了。

④notify 通知的顺序不能错

假设在线程A中执行wait(),在线程B中执行notify()。但若是线程B先执行了notify()而后结束了,线程A才去执行wait(),那此时,线程A将没法被正常唤醒了(还能够经过③中提到的interrupt()方法以抛出异常的方式唤醒^~^)。

这篇文章: JAVA多线程之线程间的通讯方式中的第③点提到了notify通知顺序出错会致使 调用wait()进入等待队列的线程再也没法被唤醒了。

⑤多线程中测试某个条件的变化用 if 仍是用 while?

有两个线程从List中删除数据,而只有一个线程向List中添加数据。初始时,List为空,只有往List中添加了数据以后,才能删除List中的数据。添加数据的线程向List添加完数据后,调用notifyAll(),唤醒了两个删除线程,可是它只添加了一个数据,而如今有两个唤醒的删除线程,这时怎么办??

若是用 if 测试List中的数据的个数,则会出现IndexOutofBoundException,越界异常。缘由是,List中只有一个数据,第一个删除线程把数据删除后,第二个线程再去执行删除操做时,删除失败,从而抛出 IndexOutofBoundException。

可是若是用while 测试List中数据的个数,则不会出现越界异常!!!神奇。

当wait等待的条件发生变化时,会形成程序的逻辑混乱---即,List中没有数据了,再仍是有线程去执行删除数据的操做。所以,须要用while循环来判断条件的变化,而不是用if。

示例以下:Add类,负责添加数据:

public class Add {

    private String lock;
    public Add(String lock) {
        super();
        this.lock = lock;
    }

    public void add() {
        synchronized (lock) {
            ValueObject.list.add("anyString");
            lock.notifyAll();
        }
    }
}

public class ThreadAdd extends Thread {

    private Add p;

    public ThreadAdd(Add p) {
        super();
        this.p = p;
    }

    @Override
    public void run() {
        p.add();
    }
}

Subtract类,负责删除数据----先要进行条件判断,而后执行wait(),这意味着:wait等待的条件可能发生变化!!!

public class Subtract {

    private String lock;

    public Subtract(String lock) {
        super();
        this.lock = lock;
    }

    public void subtract() {
        try {
            synchronized (lock) {
                if(ValueObject.list.size() == 0) {//将这里的if改为while便可保证不出现越界异常!!!!
                    System.out.println("wait begin ThreadName="
                            + Thread.currentThread().getName());
                    lock.wait();
                    System.out.println("wait   end ThreadName="
                            + Thread.currentThread().getName());
                }
                ValueObject.list.remove(0);
                System.out.println("list size=" + ValueObject.list.size());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadSubtract extends Thread {

    private Subtract r;

    public ThreadSubtract(Subtract r) {
        super();
        this.r = r;
    }

    @Override
    public void run() {
        r.subtract();
    }
}

封装的List队列:

public class ValueObject {

    public static List list = new ArrayList();

}

测试类:

public class Run {

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

        String lock = new String("");

        Add add = new Add(lock);
        Subtract subtract = new Subtract(lock);

        ThreadSubtract subtract1Thread = new ThreadSubtract(subtract);
        subtract1Thread.setName("subtract1Thread");
        subtract1Thread.start();

        ThreadSubtract subtract2Thread = new ThreadSubtract(subtract);
        subtract2Thread.setName("subtract2Thread");
        subtract2Thread.start();

        Thread.sleep(1000);

        ThreadAdd addThread = new ThreadAdd(add);
        addThread.setName("addThread");
        addThread.start();

    }
}
相关文章
相关标签/搜索