Android-Java多线程通讯(生产者 消费者)&10条线程对-等待唤醒/机制的管理

上一篇博客 Android-Java多线程通讯(生产者 消费者)&等待唤醒机制 是两条线程(Thread-0 / Thread-1) 在被CPU随机切换执行;

而今天这篇博客是,在上一篇博客Android-Java多线程通讯(生产者 消费者)&等待唤醒机制 的基础上,扩大规模增加10条线程去执行 生产者 消费者;

注意:?? 上一篇博客是两条线程在执行(生产者 消费者)例如:当Thread-0 锁.wait(); 等待 冻结后,  Thread-1 锁.notify(); 唤醒的一定是 Thread-0(因为Thread-1 锁.notify(); 后

会在线程池里面 根据同步锁??找到冻结wait();的线程,而在共享的同步锁??中只有Thread-0),所以根据同步锁 就找到了 Thread-0;  唤醒成功。

所以根据以上分析,所以两条线程去执行生产者 消费者 是没有问题的:

案例一(两条线程去执行生产者 消费者);

package android.java.thread16;

/**
 * 描述资源
 */
class Res {

    /**
     * name 是共享数据,被Thread-0 Thread-1公用使用
     */
    private String name;

    /**
     * id 是共享数据,被Thread-0 Thread-1公用使用
     */
    private int id;

    /**
     * flag 是共享数据,被Thread-0 Thread-1公用使用
     */
    private boolean flag; // 定义标记 默认第一次为false

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void put(String name) {

        /**
         * 生产之前判断标记
         */
        if (!flag) {

            // 开始生产
            id += 1;
            this.name = name + " 商品编号:" + id;
            System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.name);
            // 生产完毕

            /**
             * 修改标记
             */
            flag = true;

            /**
             * 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的
             */
            notify(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着

            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void out() {

        /**
         * 消费之前判断标记
         */
        if (flag) {

            // 开始消费
            System.out.println(Thread.currentThread().getName() +  ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.name);
            // 消费完毕

            /**
             * 修改标记
             */
            flag = false;

            /**
             * 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的
             */
            notify(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着

            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 描述生产者任务
 */
class ProduceRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ProduceRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            res.put("面包??");
        }
    }
}

/**
 * 描述消费者任务
 */
class ConsumeRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ConsumeRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            res.out();
        }
    }
}

/**
 * 多线程通讯案例
 */
public class ThreadCommunicationDemo {

    public static void main(String[] args) {
        // 创建资源对象
        Res res = new Res();

        // 创建生产者任务
        ProduceRunnable produceRunnable = new ProduceRunnable(res);

        // 创建消费者任务
        ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);

        // 启动生产者任务
        new Thread(produceRunnable).start();

        // 启动消费者任务
        new Thread(consumeRunnable).start();
    }

}

执行结果,和谐了 生产一个 消费一个:


以上案例一,由于只有两条线程去执行生产者 消费者,所以 锁.notify(); 唤醒的线程只有一个,所以 以上案例一是没有问题的;

案例二:以下就使用10条线程去 执行生产者 消费者,会出现 锁.notify(); 把自身线程唤醒,出现混乱错误??

package android.java.thread18;

/**
 * 描述资源
 */
class Res {

    /**
     * name 是共享数据,被Thread-0 Thread-1公用使用
     */
    private String name;

    /**
     * id 是共享数据,被Thread-0 Thread-1公用使用
     */
    private int id;

    /**
     * flag 是共享数据,被Thread-0 Thread-1公用使用
     */
    private boolean flag; // 定义标记 默认第一次为false

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void put(String name) {

        /**
         * 生产之前判断标记
         */
        if (!flag) {
            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 开始生产
        id += 1;
        this.name = name + " 商品编号:" + id;
        System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.name);
        // 生产完毕

        /**
         * 修改标记
         */
        flag = false;

        /**
         * 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的
         */
        notify(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
    }

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void out() {

        /**
         * 消费之前判断标记
         */
        if (flag) {
            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 开始消费
        System.out.println(Thread.currentThread().getName() +  ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.name);
        // 消费完毕

        /**
         * 修改标记
         */
        flag = true;

        /**
         * 唤醒 wait(); 冻结的线程,如果没有就是空唤醒,Java是支持的
         */
        notify(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
    }
}

/**
 * 描述生产者任务
 */
class ProduceRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ProduceRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        while(true) {
            res.put("面包??");
        }
    }
}

/**
 * 描述消费者任务
 */
class ConsumeRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ConsumeRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        while (true) {
            res.out();
        }
    }
}

/**
 * 多线程通讯案例 -->>> 10条线程对 多生产 多消费 对等待/唤醒的管理控制
 */
public class ThreadCommunicationDemo {

    public static void main(String[] args) {
        // 创建资源对象
        Res res = new Res();

        // 创建生产者任务
        ProduceRunnable produceRunnable = new ProduceRunnable(res);

        // 创建消费者任务
        ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);

        // 启动生产者任务
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();

        // 启动消费者任务
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
    }

}

执行结果:

出现异常的原因:

由于有10个线程在执行 生产者 消费者,就会造成 notify();混乱 例如:生产者 生产完毕后 Thread-5>>>wait(); Thread-5>>>notify(); 的时候有可能会把自身线程Thread-5唤醒;
由于有10个线程在执行 生产者 消费者,就会造成 notify();混乱 例如:消费者 消费完毕后 Thread-8>>>wait(); Thread-8>>>notify(); 的时候有可能会把自身线程Thread-8唤醒;

当把自身线程Thread-*唤醒的时候,就已经是?错误的,异常的程序了;


案例三,主要是解决以上案例二的问题:

【修改点1:把if 修改成 while】
【修改点2:修改成notifyAll】
package android.java.thread18;

/**
 * 描述资源
 */
class Res {

    /**
     * name 是共享数据,被Thread-0 Thread-1公用使用
     */
    private String name;

    /**
     * id 是共享数据,被Thread-0 Thread-1公用使用
     */
    private int id;

    /**
     * flag 是共享数据,被Thread-0 Thread-1公用使用
     */
    private boolean flag; // 定义标记 默认第一次为false

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void put(String name) {

        /**
         * 生产之前判断标记,【修改点1:把if 修改成 while】
         */
        while (!flag) {
            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 开始生产
        id += 1;
        this.name = name + " 商品编号:" + id;
        System.out.println(Thread.currentThread().getName() + "生产者 生产了:" + this.name);
        // 生产完毕

        /**
         * 修改标记
         */
        flag = false;

        /**
         * 唤醒所有 【修改点2:修改成notifyAll】
         */
        notifyAll(); // 注意:?? wait();  notify(); notifyAll 这些必须要有同步锁包裹着
    }

    /**
     * 对操作共享数据的地方加入同步锁的方式来解决安全问题
     * public synchronized(this) void put(String name) {
     */
    public synchronized void out() {

        /**
         * 消费之前判断标记,【修改点1:把if 修改成 while】
         */
        while (flag) {
            /**
             * 当前自己线程 冻结,释放CPU执行资格,释放CPU执行权,CPU就会去执行其他线程了
             */
            try {
                wait(); // 注意:?? wait();  notify();  这些必须要有同步锁包裹着
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 开始消费
        System.out.println(Thread.currentThread().getName() +  ">>>>>>>>>>>>>>>>>>>>>>>>>>>>> 消费者 消费了:" + this.name);
        // 消费完毕

        /**
         * 修改标记
         */
        flag = true;

        /**
         * 唤醒所有 【修改点2:修改成notifyAll】
         */
        notifyAll(); // 注意:?? wait();  notify(); notifyAll 这些必须要有同步锁包裹着
    }
}

/**
 * 描述生产者任务
 */
class ProduceRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ProduceRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        while(true) {
            res.put("面包??");
        }
    }
}

/**
 * 描述消费者任务
 */
class ConsumeRunnable implements Runnable {

    /**
     * 此变量已经不是共享数据了,因为:
     *              new Thread(produceRunnable).start();
     *              new Thread(consumeRunnable).start();
     *
     * 所以:Thread-0有自己的res     Thread-1也有自己的res
     */
    private Res res;

    ConsumeRunnable(Res res) {
        this.res = res;
    }

    /**
     * 执行线程任务
     */
    @Override
    public void run() {
        while (true) {
            res.out();
        }
    }
}

/**
 * 多线程通讯案例 -->>> 10条线程对 多生产 多消费 对等待/唤醒的管理控制
 */
public class ThreadCommunicationDemo {

    public static void main(String[] args) {
        // 创建资源对象
        Res res = new Res();

        // 创建生产者任务
        ProduceRunnable produceRunnable = new ProduceRunnable(res);

        // 创建消费者任务
        ConsumeRunnable consumeRunnable = new ConsumeRunnable(res);

        // 启动生产者任务
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();
        new Thread(produceRunnable).start();

        // 启动消费者任务
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
        new Thread(consumeRunnable).start();
    }

}

执行结果:10条线程对-等待唤醒/机制的管理 比较OK??

1.修改出现问题的代码:由于每次锁.wait(); 之前是使用if判断,if判断会引发 "if (flag) {此时CPU就在这里临时阻塞了,当再次获得CPU执行权时 就属于异常了}"   所以修改每次锁.wait(); 之前判断 使用循环while(flag){} while会往回循环判断,就可以解决此问题了;

2.修改出现问题的代码:锁.notify();后 会唤醒同步锁??里面冻结的任意一个线程,有可能唤醒自身线程,有可能唤醒其他线程,现在由于加了while(flag){}来判断,唤醒自身线程会拦截了,不用担心自身线程被唤醒了,就算唤醒自身线程也会拦截;        现在愁的是(锁.notify(); 如果唤醒自身线程,就无法唤醒其他线程了)  所以解决方案是:全部唤醒 notifyAll(); 反正已经修改了while(falg){} 判断标记拦截方式了

notify();   notifyAll();    wait();   必须在 synchronized{里面使用,因为 这些方法需要 同步锁}

原文地址:https://www.cnblogs.com/android-deli/p/10244441.html

时间: 2024-10-09 21:59:04

Android-Java多线程通讯(生产者 消费者)&10条线程对-等待唤醒/机制的管理的相关文章

JAVA多线程之生产者消费者

生产者消费者并发编程: 假设仓库有10个仓位,分别有10个生产者和10个消费者,生产者不断生产产品,放入仓库的仓位中,而消费者则不断从仓库中获取产品, 如果仓库已满,则生产者要等待,等消费者消费后,空出仓位后,再继续放入产品. 反之如果仓库已空,则消费者要等待,等待生产者生产出产品后,再继续消费产品. 关于生产者.消费者有四种实现方式 1,wait,nofity方式 2,ReentrantLock锁的await()和signal() 3,阻塞队列的方式 4,Semaphore 信号量方式 下面分

java多线程模拟生产者消费者问题,公司面试常常问的题。。。

package com.cn.test3; //java多线程模拟生产者消费者问题 //ProducerConsumer是主类,Producer生产者,Consumer消费者,Product产品 //Storage仓库 //批注:我把输出结果写在程序以下了,你能够看一下,事实上非常easy的,你想象一下产品从生产,到取出的一个生产线,我们定义两个线程,生产者线程,和消费者线程,一个是生产者不停的生产产品并放入数量有限的指定槽内,而消费者从指定槽依次取出产品,现实中的流水车间也相似于此. publ

java多线程解决生产者消费者问题

import java.util.ArrayList; import java.util.List; /** * Created by ccc on 16-4-27. */ public class Test { public static void main(String[] args) { GunClip clip = new GunClip(); Producer p = new Producer(clip); customer c = new customer(clip); p.star

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制

JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是class对象 我们在上节验证了同步函数的锁是this,但是对于静态同步函数,你又知道多少呢? 我们做一个这样的小实验,我们给show方法加上static关键字去修饰 private static synchronized void show() { if (tick > 0) { try { Thread

java多线程--“升级版”生产者消费者

ReentrantLock介绍 ReentrantLock是一个可重入的互斥锁,又被称为"独占锁". 顾名思义,ReentrantLock锁在同一个时间点只能被一个线程锁持有:而可重入的意思是,ReentrantLock锁,可以被单个线程多次获取. ReentrantLock分为"公平锁"和"非公平锁".它们的区别体现在获取锁的机制上是否公平."锁"是为了保护竞争资源,防止多个线程同时操作线程而出错,ReentrantLock

Java多线程-----实现生产者消费者模式的几种方式

   1 生产者消费者模式概述 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理, 直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力.这个阻塞队列就是用来给生产者和消费者解耦的. 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度

JAVA多线程之生产者消费者模式

一.什么是生产者消费者模式? 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力. 二.为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程.在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产

Java多线程_生产者消费者模式1

生产者消费者模型       具体来讲,就是在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者把资料做成产品.生产消费者模式如下图.(图片来自网络,侵删!) 生产者消费者模型的实现 生产者是一堆线程,消费者是另一堆线程,内存缓冲区可以使用List数组队列,数据类型只需要定义一个简单的类就好.关键是如何处理多线程之间的协作.这其实也是多线程通信的一个范例. 在这个模型中,最关键就是内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者

Java多线程:生产者消费者更佳的解决方法(确定不会出现死锁)

今天看了一片博文,讲Java多线程之线程的协作,其中作者用程序实例说明了生产者和消费者问题,但我及其他读者发现程序多跑几次还是会出现死锁,百度搜了下大都数的例子也都存在bug,经过仔细研究发现其中的问题,并解决了,感觉有意义贴出来分享下. 下面首先贴出的是有bug的代码,一个4个类,Plate.java: package CreatorAndConsumer; import java.util.ArrayList; import java.util.List; /** * 盘子,表示共享的资源