多线程高级讲解四: 多线程之间的通讯 (wait、notify)

我在写这个例子的时候,也出了很多问题。

今天头很痛,好长时间没弄出来,心情异常的烦躁。

 

先说下概念吧:

多线程之前通讯,不要想复杂了。他就是一个生产者 与 消费者的概念。

 

比如说一个生活中的例子(这是基于我的理解自己想的一个例子):

在银行办业务,需要排队。 业务员属于一个线程,办业务的人属于一个线程。

那么在程序里就相当于:业务员刚坐下开始上班,就处于一个wait状态(等待),然后业务员就对外面的人立个牌子,空闲中。这就相当于notify,就是告诉外面的人,我这里可以办业务。 然后来银行存钱的人发现,业务员立了个空闲中的牌子,然后存钱的人就从wait(等待状态变成了)开始去办业务的状态,当这个存钱的人业务办完了,就告诉柜台的业务员我办好了。那么这个过程也就是 notify ,然后业务员知道了,准备接待下一位存钱的人,业务员又处于一个wait状态了。 就这样循环下去。

所以: wait、notify就是一个是等待,一个是通知的意思。

 

 

如果不用,wait和notify会出现什么情况呢 ?

比如:一个人在存钱的时候,来了个不讲理的人,人家的钱还没存,刚要存的时候,这个不讲理的人就说,先给我存钱。这时候,银行业务员可能擡头看见了,也可能没看见,拿着钱就存了。然后第3个人要取钱,卡给了,密码输了,又来了一个人,把第3个人推出去了。说了一句,取他一个亿。 这时候是不会就乱套了 ?同样也适用于业务员,不然,来了一个很热心的业务员,说你让开,让我来给这个美女办业务,上来也不知道要取钱还是存钱,想着这个美女应该是来开户的吧。所以业务就办错了。

 

所以通过上面的例子:就可以知道,很多时候需要使用到wait 和 notify。而且为了不让别人插队,需要一把锁,把进去办业务的人 和 业务员都锁起来,办好了之后,再解锁。

 

 

用代码实现,没有用 waitnotify没加锁的效果:

public class Person {

    private String username;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "username='" + username + '\'' +
                ", age=" + age +
                '}';
    }
}





public class WriteRunnableThread implements Runnable {

    private Person person;

    public WriteRunnableThread(Person person) {
        this.person = person;
    }

    public void run() {

        /**
         * 定义一个局部变量。
         * 当这个值为偶数时,为Person类赋值: 偶数   0
         * 当这个数为奇数时,为Person类赋值: 奇数   1
         */
        int data = 0;

        while (true) {
            if (data == 0) {
                person.setUsername("偶数");
                person.setAge(0);
            } else {
                person.setUsername("奇数");
                person.setAge(1);
            }

            /**
             * 依次改变data的值为: 偶数、奇数
             */
            data = (data + 1) % 2;
        }
    }

}





public class ReadRunnableThread implements Runnable {

    private Person person;

    public ReadRunnableThread(Person person) {
        this.person = person;
    }

    public void run() {

        while (true) {
            System.out.println(person.getUsername() + "," + person.getAge());
        }
    }
}


public class Test {

    /**
     * 现在要用多线程做一件事情:
     * <p>
     * 创建两个线程,一个线程向Person类写数据,一个线程从Person类中读数据。
     */
    public static void main(String[] args) {

        Person person = new Person();
        WriteRunnableThread writeRunnableThread = new WriteRunnableThread(person);
        ReadRunnableThread readRunnableThread = new ReadRunnableThread(person);

        Thread write = new Thread(writeRunnableThread);
        Thread read = new Thread(readRunnableThread);

        write.start();
        read.start();


        /**
         * 会出现的结果:
         *
         * 奇数,1
         * 奇数,0
         * 偶数,0
         * 偶数,1
         *
         * 有没有发现,这不是我们想要的结果 ?
         * 而且也会发现,拿到的数据,并不是按顺序来的。
         */
    }
}

 

 

然后,再来看,使用了wait  和 notify  并且加了同一把锁的效果:

public class Person {

    public String username;
    public int age;

    /**
     * true 生产者线程等待,   false 消费者线程等待
     */
    public boolean flag = false;
}




public class WriteThread extends Thread {

    private Person person;

    public WriteThread(Person person) {
        this.person = person;
    }

    @Override
    public void run() {

        /**
         * 定义一个局部变量。
         * 当这个值为偶数时,为Person类赋值: 偶数   0
         * 当这个数为奇数时,为Person类赋值: 奇数   1
         */
        int data = 0;

       while (true){

            synchronized (person) {

                /**
                 * true: 等待消费
                 */
                if (person.flag) {
                    try {
                        person.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                if (data == 0) {
                    person.username = "偶数";
                    person.age = 0;
                } else {
                    person.username = "奇数";
                    person.age = 1;
                }

                /**
                 * 依次改变data的值为: 偶数、奇数
                 */
                data = (data + 1) % 2;

                person.flag = false;
                person.notify();
            }
        }

    }
}





public class ReadThread extends Thread {

    public Person person;

    public ReadThread(Person person) {
        this.person = person;
    }

    @Override
    public void run() {

        while (true){

            synchronized (person) {

                if (!person.flag) {
                    try {
                        person.wait();
                    } catch (InterruptedException e) {
                    }
                }
                System.out.println(person.username + "," + person.age);
                person.flag = true;
                person.notify();
            }
        }
    }
}





public class Test {

    /**
     * 现在要用多线程做一件事情:
     * <p>
     * 创建两个线程,一个线程向Person类写数据,一个线程从Person类中读数据。
     * <p>
     * 写这个例子的时候报了很多错:
     * 1. notify 没有在synchronized代码块中报错。
     * 2. 没有使用 synchronized 也报错了。
     * 3. 没有使用对象锁,也报错了。
     *
     * 所以:
     * 使用wait 和 notify,一定要在同步代码块中,同时存在。
     * 因为wait 和 notify 在Object内中,所以一定要使用同一个对象锁。
     */
    public static void main(String[] args) {

        Person person = new Person();

        WriteThread writeThread = new WriteThread(person);
        ReadThread readThread = new ReadThread(person);

        writeThread.start();
        readThread.start();
    }
}

 

上面就是使用 wait  notify   加锁的代码实现。

重点:(想了解这个可以去看   多线程高级讲解五: 多线程的Lock锁,多线程同步、多线程并发的概念

wait 、notify只能在synchronized中使用,不能在jdk1.5的Lock锁中使用。

在Lock锁中,如果要线程之间通讯,需要使用Condition类中的await 和 signal 。

 

wait 与 sleep的区别是什么 ?

他们都是做休眠的。

wait必须在synchronized同步代码块中使用,只要运行到wait的代码,下面的代码就不会执行(处于等待状态),他会等待其他线程notify的唤醒才能从休眠状态改为运行状态,通过上面的例子,还可以看出wait还可以释放锁的资源。

sleep在线程中只等待,不会释放锁的资源。

 

本来想把上面这个例子,也实现成 银行业务员  和  客户这种关系,但是今天实在不想再看电脑屏幕了。将就着看吧。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章