zoukankan      html  css  js  c++  java
  • Thread(生产者和消费者) wait、notify、notifyAll

    在java中,线程间的通信可以使用wait、notify、notifyAll来进行控制。从名字就可以看出来这3个方法都是跟多线程相关的,但是可能让你感到吃惊的是:这3个方法并不是Thread类或者是Runnable接口的方法,而是Object类的3个本地方法。

    其实要理解这一点也并不难,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:java.lang.IllegalMonitorStateException:current thread not owner

    也就是说,在调用这3个方法的时候,当前线程必须获得这个对象的锁,那么这3个方法就是和对象锁相关的,所以是属于Object的方法而不是Thread,因为不是每个对象都是Thread。所以我们在理解wait、notify、notifyAll之前,先要了解以下对象锁。

    多个线程都持有同一个对象的时候,如果都要进入synchronized(obj){......}的内部,就必须拿到这个对象的锁,synchronized的机制保证了同一时间最多只能有1个线程拿到了对象的锁,如下图:

    下面我们来看一下这3个方法的作用:
    wait:线程自动释放其占有的对象锁,并等待notify
    notify:唤醒一个正在wait当前对象锁的线程,并让它拿到对象锁
    notifyAll:唤醒所有正在wait前对象锁的线程

    notify和notifyAll的最主要的区别是:notify只是唤醒一个正在wait当前对象锁的线程,而notifyAll唤醒所有。值得注意的是:notify是本地方法,具体唤醒哪一个线程由虚拟机控制;notifyAll后并不是所有的线程都能马上往下执行,它们只是跳出了wait状态,接下来它们还会是竞争对象锁。

    下面通过一个常用生产者、消费者的例子来说明。
    package com.gdut.lang.thread;
    
    public class ProducerAndConsumer {
    
        public static void main(String[] args) {
            SyncStack ss = new SyncStack();
            Producer p = new Producer(ss);
            Consumer c = new Consumer(ss);
            
            new Thread(p).start();
            new Thread(c).start();
        }
    
    }
    
    class WoTou {
        private int id;
        
        public WoTou(int id) {
            this.id = id;
        }
        
        public String toString() {
            return "WoTou : " + id;
        }
    }
    
    class SyncStack {
        int index = 0;
        WoTou[] arrWT = new WoTou[5];
        
        public synchronized void push(WoTou wt) {
            if ( index == arrWT.length ) { //窝头数组满了
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            arrWT[index] = wt;
            index++;
            this.notify();
        }
        
        public synchronized WoTou pop() {
            
            if ( index == 0 ) { //数组里面没窝头了
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            index--;
            this.notify();
            return arrWT[index];
            
        }
    }
    
    class Producer implements Runnable {
        SyncStack ss = null;
        public Producer(SyncStack ss) {
            this.ss = ss;
        }
        
        public void run() {
            for (int i = 0; i < 20; i++) {
                WoTou wt = new WoTou(i);
                ss.push(wt);
                System.out.println("Producer: " + wt);
                try {
                    Thread.sleep((int)Math.random()*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
    
    class Consumer implements Runnable {
        SyncStack ss = null;
        public Consumer(SyncStack ss) {
            this.ss = ss;
        }
        
        @Override
        public void run() {
            for ( int i = 0; i < 20; i++ ) {
                WoTou wt = ss.pop();
                System.out.println("Consumer: " + wt);
                try {
                    Thread.sleep((int)Math.random()*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        
    }
  • 相关阅读:
    TOJ3650 Legal or Not
    Fork/Join框架之双端队列
    [每日一题] OCP1z0-047 :2013-08-26 TIMESTAMP WITH LOCAL TIME ZONE....................112
    [C++基础]C++中静态成员函数如何访问非静态成员
    HDU 1504 Disk Tree
    Android Checkbox Example
    STM32F103定时器输出PWM波控制直流电机
    SICP 习题 (1.7) 解题总结
    Linux磁盘及文件系统管理 4---- Linux文件系统挂载管理
    推荐《Linux 多线程服务器端编程》
  • 原文地址:https://www.cnblogs.com/myseries/p/7492631.html
Copyright © 2011-2022 走看看