zoukankan      html  css  js  c++  java
  • 线程通信

    第一节  线程通信

    1.1 线程通信引入

    应用场景:生产者和消费者问题

    • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
    • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
    • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止
    • 分析
      • 这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
      • 对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费
      • 对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费
      • 在生产者消费者问题中,仅有synchronized是不够的
        • synchronized可阻止并发更新同一个共享资源,实现了同步
        • synchronized不能用来实现不同线程之间的消息传递(通信)
    • Java提供了3个方法解决线程之间的通信问题 

     

    方法名

    作  用

    final void wait()

    表示线程一直等待,直到其它线程通知

    void wait(long timeout)

    线程等待指定毫秒参数的时间

    final void wait(long timeout,int nanos)

    线程等待指定毫秒、微妙的时间

    final void notify()

    唤醒一个处于等待状态的线程

    final void notifyAll()

    唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先运行

    注意事项

    q 均是java.lang.Object类的方法

    q 都只能在同步方法或者同步代码块中使用,否则会抛出异常

    【示例1】 准备生产者消费者问题

      

    /**
     * 商品类
     */
    public class Product {
        private String name;//馒头、玉米饼
        private String color;//白色  黄色
        public Product() {
        }
        public Product(String name, String color) {
            this.name = name;
            this.color = color;
        }

    //…..省略getter和setter方法
        public String toString() {
            return "Product{" +
                    "name='" + name + '\'' +
                    ", color='" + color + '\'' +
                    '}';
        }
    }

    /**
     * 生产者线程
     */
    public class ProduceRunnable implements  Runnable {
        //private  Product product = new Product();
        private Product product;
        public void setProduct(Product product) {
            this.product = product;
        }
        public void run() {
            int i = 0;
            while(true){
                if(i%2==0){
                    product.setName("馒头");
                    product.setColor("白色");
                }else{
                    product.setName("玉米饼");
                    product.setColor("黄色");
                }
                System.out.println("生产者生产商品"+product.getName()
                        +"  "+product.getColor());
                i++;
            }
        }
    }

    /**
     * 消费者线程
     */
    public class ConsumeRunnable implements Runnable {
        //private Product product = new Product();
        private Product product;
        public ConsumeRunnable() {
        }
        public ConsumeRunnable(Product product) {
            this.product = product;
        }
        public void setProduct(Product product) {
            this.product = product;
        }
        public void run() {
            while(true){
                System.out.println("消费者消费商品"+product.getName()
                        +"  "+product.getColor());
            }
        }
    }

    public class Test {
        public static void main(String[] args) {
            Product product = new Product();


            ProduceRunnable runnable1 = new ProduceRunnable();
            runnable1.setProduct(product);
            Thread thread1 = new Thread(runnable1);

            Runnable runnable2 = new ConsumeRunnable(product);
            Thread thread2 = new Thread(runnable2);


            thread1.start();
            thread2.start();
        }
    }

    注意:必须保证生产者消费者操作的是一个商品对象,否则就会出现消费者消费null的情况。

    Product product = new Product();

    ProduceRunnable runnable1 = new ProduceRunnable();
    runnable1.setProduct(product);
    Runnable runnable2 = new ConsumeRunnable(product);

    1.2使用同步代码块实现线程同步

    【示例2】 使用同步代码块保证线程安全

    public class ProduceRunnable implements  Runnable {
        private Product product;
        public void setProduct(Product product) {
            this.product = product;
        }
       public void run() {
            int i = 0;
            while(true){
                synchronized (product){
                    if(i%2==0){
                        product.setName("馒头");
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        product.setColor("白色");
                    }else{
                        product.setName("玉米饼");
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        product.setColor("黄色");
                    }
                  System.out.println("生产者生产商品"+product.getName()

    +"  "+product.getColor());
                }
                i++;
            }
        }
    }

    public class ConsumeRunnable implements Runnable {
        private Product product;
        public void run() {
            while(true){
                synchronized (product){
                    System.out.println("消费者消费商品"

    +product.getName()+"  "+product.getColor());
                }
            }
        }
    }

    • 注意:不仅生产者要加锁,而且消费者也要加锁,并且必须是一把锁(不仅是一个引用变量,而且必须是指向同一个对象)

    1.3实现线程通信-同步代码块形式

    【示例3】 实现线程通信同步代码块方式

    public class ProduceRunnable implements  Runnable {
        public void run() {
            int i = 0;
            while(true){
                synchronized (product){
                    //如果已经有商品,就等待
                    if(product.flag){
                        try {
                            product.wait(); //必须调用同步监视器的通信方法
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //生产商品
                    if(i%2==0){
                        product.setName("馒头");
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        product.setColor("白色");
                    }else{
                        product.setName("玉米饼");
                        try {
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        product.setColor("黄色");
                    }
                    //输出结果
                  System.out.println("生产者生产商品"+product.getName()
                            +"  "+product.getColor());
                    //修改一下商品的状态
                    product.flag = true;
                    //通知消费者来消费
                    product.notify();
                }

                i++;
            }
        }
    }

    public class ConsumeRunnable implements Runnable {
        public void run() {
            while(true){
                synchronized (product){
                    //如果没有商品,就等待
                    if(!product.flag){
                        try {
                            product.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //消费商品
                    System.out.println("消费者消费商品"
                          +product.getName()+"  "+product.getColor());
                    //修改商品的状态
                    product.flag = false;
                    //通知生产者进行生产
                    product.notifyAll();
                }
            }
        }
    }

    线程同步的细节
       细节1:进行线程通信的多个线程,要使用同一个同步监视器(product),还必须要调用该同步监视器的wait()notify()notifyAll();       
       细节2:线程通信的三个方法
          wait() 等待
          在【其他线程】调用【此对象】的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。当前线程必须拥有此对象监视器。
          wait(time) 等待
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 当前线程必须拥有此对象监视器。
         notify() 通知  唤醒          
         唤醒在【此对象监视器】上等待的【单个】线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。【选择是任意性的】,并在对实现做出决定时发生    
          notifyAll() 通知所有  唤醒所有

    唤醒在【此对象监视器】上等待的【所有】线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程【进行竞争】;     
       细节3:完整的线程生命周期
           阻塞状态有三种
             普通的阻塞  sleep,join,Scanner input.next()
             同步阻塞(锁池队列) 没有获取同步监视器的线程的队列
             等待阻塞(阻塞队列) 被调用了wait()后释放锁,然后进行该队列

      
       


       细节4sleep()wait()的区别
          区别1:sleep()  线程会让出CPU进入阻塞状态,但不会释放对象锁  
              wait() 线程会让出CPU进入阻塞状态, 【也会放弃对象锁】,进入等待【此对象】的等待锁定池
          区别2: 进入的阻塞状态也是不同的队列
          区别3:wait只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用

  • 相关阅读:
    完全背包 基础
    hdoj_2546饭卡(强忍悲痛,好好写题解)
    蓝桥杯--猜字母 学到了!
    Common Subsequence
    Ansible ad-hoc 手册(3)
    ansible playbook 以及变量(2)
    Ansible安装以及常用模块操作(1)
    zabbix3.4自定义触发器(4)
    zabbix3.4自定义监控项(3)
    zabbix3.4监控一台主机(2)
  • 原文地址:https://www.cnblogs.com/vincentmax/p/14246727.html
Copyright © 2011-2022 走看看