多线程:通信

线程通信:多个线程之间进行信息交流与传递。常用于公共数据的通信。

下面我们分别演示一下单消费者、单生产者,多消费者、多生产者的通信。

在演示之前,强调一下:多个线程访问同一个类的不同synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了java的内置锁, 即锁住的是方法所属对象本身。也就是说,多个线程不能同时(并发)访问同一个同步方法,也不能同时(并发)访问不同的同步方法。

消费者、生产者(1-1)示例

1.面包类

public class Breads {
    /**
     * 面包的id
     */
    private int bid;
    /**
     * 面包的个数
     */
    private int num;

    /**
     * 无参构造
     */
    public Breads() {
    }

    /**
     * 有参构造
     */
    public Breads(int bid, int num) {
        this.bid = bid;
        this.num = num;
    }

    /**
     * 生产面包
     * synchronized保证值进行生产面包或消费面包(并发线程中串行执行)
     */
    public synchronized void pro() {
        if (num != 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num = num + 1;
        bid = bid + 1;
        System.out.println(Thread.currentThread().getName() + "生产了一个编号为" + bid + "的面包!个数为" + num);
        //2.通知等待的消费线程消费面包(当前线程既然执行到这里了,说明它没有处于等待状态,一般来说就是指当前线程唤醒其它等待线程)
        notify();
    }

    /**
     * 消费面包
     * synchronized保证值进行生产面包或消费面包(并发线程中串行执行)
     */
    public synchronized void consume() {
        //1.假设消费线程先进入,判断面包数量为0,进入阻塞等待,强制生产线程生产面包
        if (num == 0) {
            try {
                //等同于this.wait()
                wait();
                System.out.println("xxxxxxxxxxxxxxxxxxx");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //3.消费线程得到通知,继续执行,num=1-1=0,消费了一个面包
        num = num - 1;
        System.out.println(Thread.currentThread().getName() + "买了一个编号为" + bid + "的面包!个数还有" + num);
        notify();
    }
}

2.生产者

public class Producer implements Runnable {
    private Breads bre;

    /**
     * 无参构造
     */
    public Producer() {
    }

    /**
     * 初始化公共资源
     */
    public Producer(Breads bre) {
        this.bre = bre;
    }

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

    /**
     * 任务:生产面包
     */
    private void p() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            bre.pro();
        }
    }
}

3.消费者

public class Consumer implements Runnable {
    private Breads bre;

    /**
     * 无参构造
     */
    public Consumer() {
    }

    /**
     * 初始化公共资源
     */
    public Consumer(Breads bre) {
        this.bre = bre;
    }

    @Override
    public void run() {
        c();
    }

    /**
     * 任务:消费面包
     */
    private void c() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            bre.consume();
        }
    }
}

 4.测试类

public class Test {
    /**
     * 打印结果:
     * 生产者生产了一个编号为1的面包!个数为1
     * 消费者买了一个编号为1的面包!个数还有0
     * 生产者生产了一个编号为2的面包!个数为1
     * xxxxxxxxxxxxxxxxxxx
     * 消费者买了一个编号为2的面包!个数还有0
     * 生产者生产了一个编号为3的面包!个数为1
     * 消费者买了一个编号为3的面包!个数还有0
     * 生产者生产了一个编号为4的面包!个数为1
     * xxxxxxxxxxxxxxxxxxx
     * 消费者买了一个编号为4的面包!个数还有0
     * 生产者生产了一个编号为5的面包!个数为1
     * xxxxxxxxxxxxxxxxxxx
     * 消费者买了一个编号为5的面包!个数还有0
     * 分析: 调用wait()的线程,在被唤醒以后,会继续执行。
     */
    public static void main(String[] args) {
        Breads bre = new Breads();
        //创建基于同一个面包实例的线程(共享变量)
        Producer pro = new Producer(bre);
        Consumer con = new Consumer(bre);

        //定义生产者线程
        Thread t1 = new Thread(pro, "生产者");
        //定义消费者线程
        Thread t2 = new Thread(con, "消费者");
        t1.start();
        t2.start();
    }
}

消费者、生产者(N-N)示例

1.面包类

public class Breads {
    /**
     * 面包的id
     */
    private int bid;
    /**
     * 面包的个数
     */
    private int num;

    /**
     * 无参构造
     */
    public Breads() {
    }

    /**
     * 有参构造
     */
    public Breads(int bid, int num) {
        this.bid = bid;
        this.num = num;
    }

    /**
     * 生产面包
     * synchronized保证只进行生产面包或消费面包(并发线程中串行执行)
     * 多个线程访问同一个synchronized方法,只有一个线程能够进入该方法。
     * 多个线程访问同一个类的不同synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了java的内置锁, 即锁住的是方法所属对象本身。
     */
    public synchronized void pro() {
        //while->if:会出现负数消费
        //while:循环的作用就是阻塞的线程们被唤醒后,保证只有一个生产线程进行生产,后续执行的线程会重新进入阻塞。
//        if (num != 0) {
        while (num != 0) {
            try {
//                System.out.println(Thread.currentThread().getName()+"-----------------pro-wait");
                //释放锁,cpu重新调度执行,被唤醒后继续执行,循环进入判断,如果已经生产,其它线程就不能重复生产
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num = num + 1;
        bid = bid + 1;
        System.out.println(Thread.currentThread().getName() + "生产了一个编号为" + bid + "的面包!个数为" + num);
        //通知所有等待的消费线程消费面包
        notifyAll();
    }

    /**
     * 消费面包
     * synchronized保证只进行生产面包或消费面包(并发线程中串行执行)
     * 多个线程访问同一个synchronized方法,只有一个线程能够进入该方法。
     * 多个线程访问同一个类的不同synchronized方法时, 都是串行执行的 ! 就算有多个cpu也不例外 ! synchronized方法使用了java的内置锁, 即锁住的是方法所属对象本身。
     */
    public synchronized void consume() {
        //while->if:会出现负数消费
        //while:循环的作用就是阻塞的线程们被唤醒后,保证只有一个消费线程进行消费,后续执行的线程会重新进入阻塞。
//        if (num == 0) {
        while (num == 0) {
            try {
                System.out.println(Thread.currentThread().getName()+"-----------------wait");
                //释放锁,cpu重新调度执行,被唤醒后继续执行,循环进入判断,如果已经消费,其它线程就不能重复消费
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num = num - 1;
        System.out.println(Thread.currentThread().getName() + "买了一个编号为" + bid + "的面包!个数还有" + num);
        //通知所有等待的生产线程生产面包
        notifyAll();
    }

    /**
     * 打印结果:
     * 生产者2生产了一个编号为1的面包!个数为1
     * 消费者2买了一个编号为1的面包!个数还有0
     * 消费者3-----------------wait
     * 消费者1-----------------wait
     * 生产者1生产了一个编号为2的面包!个数为1
     * 消费者1买了一个编号为2的面包!个数还有0
     * 消费者3买了一个编号为2的面包!个数还有-1
     * 生产者3生产了一个编号为3的面包!个数为0
     * ...
     *
     * 分析:
     * num=0,生产线程2生产面包->num=1 【notifyAll无实际效果】
     * num=1,生产线程3进入,阻塞等待,等待被唤醒,cpu重新调度执行 【释放锁】
     * num=1,消费线程2消费面包->num=0 【notifyAll无实际效果】
     * num=0,消费线程3进入->阻塞等待,等待被唤醒,cpu重新调度执行 【释放锁】
     * num=0,消费线程1进入->阻塞等待,等待被唤醒,cpu重新调度执行 【释放锁】
     * num=0,生产线程1生产面包->num=1 【notifyAll有实际效果,唤醒阻塞的消费线程3、消费线程1,唤醒顺序不保证】
     * num=1,消费线程1被唤醒->继续执行,消费面包,num=0 【notifyAll无实际效果】
     * num=1,消费线程3被唤醒->继续执行,消费面包,num=-1 【notifyAll有实际效果,唤醒阻塞的生产线程3】
     * num=-1,生产线程3被唤醒->继续执行,生产面包,num=0 【notifyAll无实际效果】
     * 结论:多消费者多生产者的情况下,出现负数消费,是因为多个消费者之前被阻塞了,还没有消费,一旦被生产者唤醒,就会相继执行。
     * 问题:如何解决这个问题?
     * 解决:将if判断改为循环判断,这样,被阻塞的线程们,被唤醒以后,继续执行,会重新进入循环判断,保证每生产/消费一次就只能消费/生产一次,想要陆续(重复)消费/生产的线程只能重新(继续)阻塞。
     */
}

2.生产者

public class Producer implements Runnable {
    private Breads bre;

    /**
     * 无参构造
     */
    public Producer() {
    }

    /**
     * 初始化公共资源
     */
    public Producer(Breads bre) {
        this.bre = bre;
    }

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

    /**
     * 任务:生产面包
     */
    private void p() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            bre.pro();
        }
    }
}

3.消费者

public class Consumer implements Runnable {
    private Breads bre;

    /**
     * 无参构造
     */
    public Consumer() {
    }

    /**
     * 初始化公共资源
     */
    public Consumer(Breads bre) {
        this.bre = bre;
    }

    @Override
    public void run() {
        c();
    }

    /**
     * 任务:消费面包
     */
    private void c() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            bre.consume();
        }
    }
}

4.测试类

public class Test {
    public static void main(String[] args) {
        Breads bre = new Breads();
        //创建基于同一个面包实例的线程(共享变量)
        Producer pro = new Producer(bre);
        Consumer con = new Consumer(bre);

        //定义生产者线程
        Thread t1 = new Thread(pro,"生产者1");
        Thread t2 = new Thread(pro,"生产者2");
        Thread t3 = new Thread(pro,"生产者3");

        //定义消费者线程
        Thread t4 = new Thread(con,"消费者1");
        Thread t5 = new Thread(con,"消费者2");
        Thread t6 = new Thread(con,"消费者3");
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值