线程方法wait()和notify()的使用

实现需求:

开启2个线程,1个线程对某个int类型成员变量加1,另外1个减1,但是要次序执行,即如果int型的成员变量是0,则输出01010101这样的结果

代码如下

package test;

public class Sample {

    private int i;

    public synchronized void increase() {

        if(i != 0) { // 值不为0时,已经加过,释放锁,等待其他线程减为0
            try {
                wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 值为0,需要增加
        i++;
        System.out.println(i);
        // 通知其他线程,可以进行减1的操作了
        notify();
    }

    public synchronized void decrease() {
        if(i == 0) { // 值为0时,已经减过,释放锁,等待其他线程加为1
            try {
                wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 值为1,需要减少
        i--;
        System.out.println(i);
        // 通知其他线程,可以进行加1的操作了
        notify();
    }

    public static void main(String[] args) {

        Sample sample = new Sample();

        Thread t1 = new IncreaseThread(sample);
        Thread t2 = new DecreaseThread(sample);

        t1.start();
        t2.start();
    }
}

class DecreaseThread extends Thread {

    private Sample sample;

    public DecreaseThread(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            sample.decrease();
        }
    }
}

class IncreaseThread extends Thread {

    private Sample sample;

    public IncreaseThread(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            sample.increase();
        }
    }
}


需求稍作改变,变成:

开启4个线程,2个线程对某个int类型成员变量加1,另外2个减1,但是要次序执行,即如果int型的成员变量是0,则输出01010101这样的结果

如果是直接再生成t3 t4分别是IncreaseThread和DecreaseThread的实例(即t1/t3为IncreaseThread类的实例,t2/t4为DecreaseThread的实例),假设执行流程如下:

(1)t2执行,由于i=0,所以线程等待,释放锁,随机通知一条线程进行执行(notify()方法是通知随机一条线程的)

(2)假设通知到了t4,由于i=0,所以t4线程又等待,锁释放,又随机通知到一条线程进行执行

(3)假设又通知到了t2线程,这个时候,线程的执行是从wait()方法后面开始执行的,不会再去判断i是否等于0了,继续执行,会进行i的自减操作,出现i=-1的局面


所以代码需要修改

package test;

public class Sample {

    private int i;

    public synchronized void increase() {

        while(i != 0) { // 值不为0时,已经加过,释放锁,等待其他线程减为0
            try {
                wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 值为0,需要增加
        i++;
        System.out.println(i);
        // 通知其他线程,可以进行减1的操作了
        notify();
    }

    public synchronized void decrease() {
        while(i == 0) { // 值为0时,已经减过,释放锁,等待其他线程加为1
            try {
                wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 值为1,需要减少
        i--;
        System.out.println(i);
        // 通知其他线程,可以进行加1的操作了
        notify();
    }

    public static void main(String[] args) {

        Sample sample = new Sample();

        Thread t1 = new IncreaseThread(sample);
        Thread t2 = new DecreaseThread(sample);
        Thread t3 = new IncreaseThread(sample);
        Thread t4 = new DecreaseThread(sample);

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class DecreaseThread extends Thread {

    private Sample sample;

    public DecreaseThread(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            sample.decrease();
        }
    }
}

class IncreaseThread extends Thread {

    private Sample sample;

    public IncreaseThread(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (Exception e) {
                e.printStackTrace();
            }

            sample.increase();
        }
    }
}

这里把if判断改成了while循环,因为wait方法之后,应该是需要重复判断一次i的情况的,这样就不会出现数字不对的情况了


这里有一条基本原则:

永远在while循环里而不是if语句下使用wait。这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知

猜你喜欢

转载自blog.csdn.net/mweibiao/article/details/80341875