Java学习笔记46(多线程三:线程之间的通信)

Posted xuyiqing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java学习笔记46(多线程三:线程之间的通信)相关的知识,希望对你有一定的参考价值。

多个线程在处理同一个资源,但是线程的任务却不相同,通过一定的手段使各个线程能有效地利用资源,

这种手段即:等待唤醒机制,又称作线程之间的通信

涉及到的方法:wait(),notify()

 

示例:

两个线程一个输入,一个输出

package demo;

public class Resource {
    public String name;
    public String sex;
}

输入线程:

package demo;

public class Input implements Runnable {
    private Resource r = new Resource();

    public void run() {
        int i = 0;
        while (true) {
            if (i % 2 == 0) {
                r.name = "张三";
                r.sex = "男";
            } else {
                r.name = "李四";
                r.sex = "女";
            }
            i++;
        }
    }

}

输出线程:

package demo;

public class Output implements Runnable {
    private Resource r = new Resource();
    public void run(){
        while (true) {
            System.out.println(r.name+"..."+r.sex);
        }
    }
}

测试类:

package demo;

public class ThreadDemo {
    public static void main(String[] args) {
        Input in = new Input();
        Output out = new Output();
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);
        
        tin.start();
        tout.start();
    }
}

 

运行后却发现输出的都是null...null

因为输入线程和输出线程中创建的Resource对象使不同的

 

解决null问题:

package demo;

public class Input implements Runnable {
    private Resource r;
    
    public Input(Resource r){
        this.r = r;
    }

    public void run() {
        int i = 0;
        while (true) {
            if (i % 2 == 0) {
                r.name = "张三";
                r.sex = "男";
            } else {
                r.name = "李四";
                r.sex = "女";
            }
            i++;
        }
    }

}
package demo;

public class Output implements Runnable {
    private Resource r;
    
    public Output(Resource r){
        this.r = r;
    }
    
    public void run(){
        while (true) {
            System.out.println(r.name+"..."+r.sex);
        }
    }
}
package demo;

public class ThreadDemo {
    public static void main(String[] args) {
        
        Resource r = new Resource();
        
        Input in = new Input(r);
        Output out = new Output(r);
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);
        
        tin.start();
        tout.start();
    }
}

 

运行后又发现了另一个问题:

输出中含有:张三...女或者李四...男,性别出错

发生原因:

赋值完张三和男后,继续赋值李四和女,这时候还未还得及赋值女,就进入了输出线程,这时候就会输出李四...男

 

于是想到加上同步:

    public void run() {
        int i = 0;
        while (true) {
            synchronized (this) {
                if (i % 2 == 0) {
                    r.name = "张三";
                    r.sex = "男";
                } else {
                    r.name = "李四";
                    r.sex = "女";
                }
                i++;
            }
        }
    }
    public void run() {
        while (true) {
            synchronized (this) {
                System.out.println(r.name + "..." + r.sex);
            }
        }
    }

 

然而问题并没有解决:

原因:

这里的同步失去了作用,用到的不是一个锁

 

解决办法:

使用一个共同的锁即可

public void run() {
        int i = 0;
        while (true) {
            synchronized (r) {
                if (i % 2 == 0) {
                    r.name = "张三";
                    r.sex = "男";
                } else {
                    r.name = "李四";
                    r.sex = "女";
                }
                i++;
            }
        }
    }
    public void run() {
        while (true) {
            synchronized (r) {
                System.out.println(r.name + "..." + r.sex);
            }
        }
    }

这时候就是正常的输出了

 但是还是存在一个问题,我们希望的是张三和李四交错出现,一个张三一个李四,现在依然是随机出现的,大片的张三或李四

 

 

解决办法:

先让input线程赋值,然后让output线程输出,并且让输入线程等待,不允许再赋值李四,等待输出张三结束后,再允许李四赋值,依次下去

输入线程也需要同样的方式,输出完后要等待

这时候就需要用到等待唤醒机制:

输入:赋值后,执行方法wait()永远等待

输出:打印后,再输出等待之前,唤醒输入notify(),自己再wait()永远等待

输入:被唤醒后,重新赋值,必须notify()唤醒输出的线程,自己再wait()等待

依次循环下去

 

代码实现:

package demo;

public class Resource {
    public String name;
    public String sex;
    public boolean flag = false;
}
package demo;

public class Input implements Runnable {
    private Resource r;

    public Input(Resource r) {
        this.r = r;
    }

    public void run() {
        int i = 0;
        while (true) {
            synchronized (r) {
                if (r.flag) {
                    try {
                        r.wait();
                    } catch (Exception e) {
                    }
                }
                if (i % 2 == 0) {
                    r.name = "张三";
                    r.sex = "男";
                } else {
                    r.name = "李四";
                    r.sex = "女";
                }
                r.flag = true;
                r.notify();
            }
            i++;
        }
    }
}
package demo;

public class Output implements Runnable {
    private Resource r;

    public Output(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            synchronized (r) {
                if (!r.flag) {
                    try {
                        r.wait();
                    } catch (Exception e) {
                    }
                }
                System.out.println(r.name + "..." + r.sex);
                r.flag = false;
                r.notify();
            }
        }
    }
}
package demo;

public class ThreadDemo {
    public static void main(String[] args) {

        Resource r = new Resource();

        Input in = new Input(r);
        Output out = new Output(r);
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);

        tin.start();
        tout.start();
    }
}

这时候就是张三李四交错输出了

完成

 

以上是关于Java学习笔记46(多线程三:线程之间的通信)的主要内容,如果未能解决你的问题,请参考以下文章

多线程编程学习三(线程间通信)

java多线程之间的通信

volatile 学习笔记

多线程Java多线程学习笔记 | 多线程基础知识

Java并发线程通信

Java多线程:线程间通信方式