zoukankan      html  css  js  c++  java
  • 多线程编程-- part 4 线程间的通信

    线程间的相互作用

      线程之间需要一些协调通信,来共同完成一件任务。

      Object类相关的方法:notify(),notifyAll(),wait()。会被所有的类继承,这些方法是final不能被重写。他们操控的是每个对象都会有的锁,如果不在synchronized里面调用wait,notify,notifyAll方法,运行时会报异常:要调用对象的这些方法,必须先获得这个对象的锁。

    wait():

      使得当前线程必须等待,进入到等待序列,等到另外一个线程调用notify()或者notifyAll()激活

      当前线程必须拥有当前对象的monitor,就是锁。线程调用wait(),释放对锁的拥有权,等待另外的线程来通知他,这样才能重新获得锁的拥有权和恢复执行。

      要确保调用wait()方法的时候拥有锁。

      调用Object.wait()方法,会释放对象的锁。调用Thread.sleep()方法,不会释放对象的锁。

    notify():

      唤醒一个等待当前对象的锁的线程,如果多个线程在等待,他们中的一个将会被选择唤醒,这种选择是随意的。

      被唤醒的锁是不能被执行的,必须要等到当前线程放弃这个对象的锁,被唤醒的线程和其他线程竞争锁。

    一个线程拥有一个对象的锁有以下几种方法:

    1.执行这个对象的synchronized实例方法

    2.执行这个对象的synchronized语句块,这个语句块锁的是这个对象

    3.对于class类的对象,执行那个类的synchronized static方法

    学到线程通信就必须看看经典例子:生产/消费问题

    1. 生产者在仓库未满的时候生产,仓库满则停止生产
    2. 消费者仅仅在仓库有产品的时候才能消费,仓空则等待
    3. 消费者发现仓库没产品消费时,通知生产者等待
    4. 生产者在生产处可消费产品时,通知等待的消费者去消费

    初步设计:

    1.Depot是“仓库”类,仓库中记录“仓库的容量(capacity)”以及“仓库中当前产品数目(size)”。

      “仓库”类的生产方法produce()和消费方法consume()方法都是synchronized方法,进入synchronized方法体,意味着这个线程获取到了该“仓库”对象的同步锁。这也就是说,同一时间,生产者和消费者线程只能有一个能运行。通过同步锁,实现了对“仓库”的互斥访问。

      produce()方法:仓库满时,生产者线程进入等待,生产完成,通过nofityAll()唤醒所有的线程,consume()方法:消费者进行消费之后,会通过nofityAll()方法通知生产者进行生产

    2.Producer类:生产者类,每次会新建一个线程调用仓库的produce()方法进行生产

    3.Customer类:消费者类,每次会新建一个线程调用仓库的consume()方法进行消费

    4.定义若干次消费和生产,看是不是满足关系

    package com.template.ProduceConsume;
    
    /**
     * <生产者和消费者>
     * <详细介绍>
     *
     * @author 姬国靖
     * @since 设计wiki | 需求wiki
     */
    public class test {
        public static void main(String[] args) {
            Depot mDepot = new Depot(100);
            Producer mPro = new Producer(mDepot);
            Customer mCus = new Customer(mDepot);
    
            mPro.produce(60);
            mPro.produce(120);
            mCus.consume(90);
            mCus.consume(150);
            mPro.produce(110);
        }
    }
    
    //仓库
    class Depot {
        private int capacity;    // 仓库的容量
        private int size;        // 仓库的实际数量
    
        public Depot(int capacity) {
            this.capacity = capacity;
            this.size = 0;
        }
    
        public synchronized void produce(int val) {
            try {
                // left 表示“想要生产的数量”(有可能生产量太多,需多此生产)
                int left = val;
                while (left > 0) {
                    // 库存已满时,等待“消费者”消费产品。
                    while (size >= capacity)
                        wait();
                    // 获取“实际生产的数量”(即库存中新增的数量)
                    // 如果“库存”+“想要生产的数量”>“总的容量”,则“实际增量”=“总的容量”-“当前容量”。(此时填满仓库)
                    // 否则“实际增量”=“想要生产的数量”
                    int inc = (size+left)> capacity ? (capacity-size) : left;
                    size += inc;
                    left -= inc;
                    System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d
    ",
                            Thread.currentThread().getName(), val, left, inc, size);
                    // 通知“消费者”可以消费了。
                    notifyAll();
                }
            } catch (InterruptedException e) {
            }
        }
    
        public synchronized void consume(int val) {
            try {
                // left 表示“客户要消费数量”(有可能消费量太大,库存不够,需多此消费)
                int left = val;
                while (left > 0) {
                    // 库存为0时,等待“生产者”生产产品。
                    while (size <= 0)
                        wait();
                    // 获取“实际消费的数量”(即库存中实际减少的数量)
                    // 如果“库存”<“客户要消费的数量”,则“实际消费量”=“库存”;
                    // 否则,“实际消费量”=“客户要消费的数量”。
                    int dec = (size<left) ? size : left;
                    size -= dec;
                    left -= dec;
                    System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d
    ",
                            Thread.currentThread().getName(), val, left, dec, size);
                    notifyAll();
                }
            } catch (InterruptedException e) {
            }
        }
    
        public String toString() {
            return "capacity:"+capacity+", actual size:"+size;
        }
    }
    
    //生产者
    class Producer {
        private Depot depot;
    
        public Producer(Depot depot) {
            this.depot = depot;
        }
    
        // 消费产品:新建一个线程向仓库中生产产品。
        public void produce(final int val) {
            new Thread() {
                public void run() {
                    depot.produce(val);
                }
            }.start();
        }
    }
    
    //消费者
    class Customer {
        private Depot depot;
    
        public Customer(Depot depot) {
            this.depot = depot;
        }
    
        // 消费产品:新建一个线程从仓库中消费产品。
        public void consume(final int val) {
            new Thread() {
                public void run() {
                    depot.consume(val);
                }
            }.start();
        }
    }

       分析:可以看出仓库满时停止生产,等待消费者通知,不够消费停止消费,,等待生产者通知

  • 相关阅读:
    研究生毕业论文如何选题
    关于虚拟机中开vmnet1(host)后pc不能上网的问题解决
    ubuntu10.04安装x264库
    CentOS 5.5无法识别Atheros AR8151网卡问题解决
    dm365工作笔记20130731
    DAVINCI DM365-DM368开发攻略——U-BOOT-2010.12及UBL的移植
    SQL语法练习
    T-SQL查询语句常用优化技巧总结
    【转】Asp.net控件开发学习笔记整理篇
    【转】Asp.net控件开发学习笔记整理篇
  • 原文地址:https://www.cnblogs.com/jijiji/p/6900994.html
Copyright © 2011-2022 走看看