45- 生产一个,消费一个,线程池

42 阅读5分钟
以前: 多个线程操作共享数据,线程任务是相同的
现在: 多个线程操作共享数据,线程任务不相同 
要求:  生产一个,消费一个,再生产一个,再消费一个
解决唤醒报异常的问题--  线程通信等待唤醒 wait()   notify() notifyAll()
/*
    定义多线程共享资源类包子类
 */
public class BaoZi04 {
    private int num;//给包子进行编号
    //添加布尔标记,true: 有包子,允许消费者消费包子,false: 没有包子,允许生产者生产包子
    private boolean flag;
    //被消费者线程调用,表示消费(打印输出)一个包子
    public synchronized void xiaoFei() {
        if (flag == false) {
            //没有包子,等待
            try {wait();}catch (Exception e){e.printStackTrace();}
        }
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName+"消费第~~~~~~"+num+"~~~~~~个包子");
        //修改标记
        flag = false;
        //唤醒生产者线程
        notify();
    }

    //被生产者线程调用,表示生产一个包子
    public synchronized void shengChan() {
        if(flag == true) {
            //有包子,等待
            try {wait();}catch (Exception e){e.printStackTrace();}
        }
        //没有包子,生产一个包子
        num++;
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName+"生产第..."+num+"...个包子");
        //修改标记
        flag = true;
        //唤醒消费者线程
        notify();
    }
    public boolean getFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
/*
    定义生产者线程任务类
 */
public class Producer04 implements Runnable{
    //定义共享资源包子BaoZi类的对象
    private BaoZi04 bz;
    //构造方法
    public Producer04(BaoZi04 bz) {
        this.bz = bz;
    }
    @Override
    public void run() {
        while (true)
            bz.shengChan();//生产一个包子
    }
}
/*
    定义消费者线程任务类
 */
public class Consumer04 implements Runnable{
    //定义共享资源包子BaoZi类的对象
    private BaoZi04 bz;

    //构造方法
    public Consumer04(BaoZi04 bz) {
        this.bz = bz;
    }

    @Override
    public void run() {
        while (true)
            bz.xiaoFei();//消费一个包子

    }
}
//测试类
/*  单生产者(生产一个包子)和单消费者(消费一个包子)

    代码优化:	运行以下测试代码:  程序出现无效的监视器状态异常。 只有作为锁的对象才能调用wait()或者notify()方法。  把生产和消费包子的功能定义在BaoZi04类中,全部定义为同步方法
 */
public class Demo04PC {
    public static void main(String[] args) {
        //创建多线程共享数据资源baoZi类的对象
        BaoZi04 bz = new BaoZi04();
        //4.创建生产者线程任务对象
        Producer04 pTask = new Producer04(bz);
        //6.创建消费者线程任务对象
        Consumer04 cTask = new Consumer04(bz);
        //6.创建生产者线程对象,传递生产者线程任务对象
        Thread pThread = new Thread(pTask, "生产者线程");
        //7.创建消费者线程对象,传递消费者线程任务对象
        Thread cThread = new Thread(cTask, "消费者线程");
        //8.生产者线程对象和消费者线程对象分别调用start方法开启线程
        pThread.start();
        cThread.start();
    }
}

线程池Thread Pool

线程频繁的创建和销毁线程,会降低效率 所以需要使用线程池 
 Callable  call  可以throws,可以try-catch
 runnable submit只能内部try-catch
 都有返回值Future   封装结果数据的  get(): 获取Future封装的结果数据  
 创建线程池对象(有3个线程对象): 工具类Executors调用静态方法
 ExecutorService pool = Executors.newFixedThreadPool(3); 创建线程池  
 shutDown():关闭线程池    
  线程池的练习计算1-100的和
    实现步骤:
        1.创建代表线程任务的Callable接口的实现类
        2.Callable接口的实现类覆盖重写抽象方法call,计算1100的数字之和
        3.创建线程池对象,指定线程数量
        4.创建多个线程任务对象
        5.线程池对象调用submit方法执行线程任务对象获取结果Future对象
        6.打印结果Future对象中封装的具体结果
 */   //执行submit方法指定的Runnable接口类型的线程任务  
  Future<?> future = pool.submit(task01);
//执行submit方法指定的Callable接口类型的线程任务
        
public class Demo07ThreadPoolSum {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //3.创建线程池对象,指定线程数量
        ExecutorService pool = Executors.newFixedThreadPool(3);
        //4.创建多个线程任务对象
        MySumTask task100 = new MySumTask(1, 100);//计算1-100数字之和的线程任务
        MySumTask task200 = new MySumTask(1, 200);//计算1-200数字之和的线程任务
        MySumTask task300 = new MySumTask(1, 300);//计算1-300数字之和的线程任务
         //3.从线程池对象pool中获取一个线程对象(至于获取到的哪个线程对象,不用管)
        //执行submit方法指定的Callable接口类型的线程任务
        //任务执行完毕,线程对象会返还池中,怎么返还的,不用关心
        //5.线程池对象调用submit方法执行线程任务对象获取结果Future对象
        Future<Integer> f = pool.submit(task100);
        //6.打印结果Future对象中封装的具体结果
        System.out.println(f.get());

        f = pool.submit(task200);
        //6.打印结果Future对象中封装的具体结果
        System.out.println(f.get());

        f = pool.submit(task300);
        //6.打印结果Future对象中封装的具体结果
        System.out.println(f.get());
    }
}

//创建Runnable接口的实现类
public class MyCallable04 implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        Random r = new Random();
        return Math.abs(r.nextInt());//返回一个正的int范围内的数字
    }
}  
/*
    计算1-100的数字之和的线程任务
 */
public class MySumTask implements Callable<Integer> {
    private int start;
    private int end;
    //满参构造方法:指定求和范围
    public MySumTask(int start, int end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        //计算指定范围内的所有数字之后
        //要求start必须<=end,但是这里代码上就不做判断
        for (int i = start; i <= end; i++) {
            sum += i;
        }
        return sum;
    }
}