现在有这个一个场景:
有一个List。就是一个缓存。
线程A负责往List中装数据。
线程B、C、D负责把数据从List中取出来,然后发送出去。
如果List中没有数据。那么线程B、C、D就调用List的wait方法进入等待状态。
如果List中的数据大于1000条,那么线程A就调用List的wait方法进入等待状态。
当线程A往List中成功装入数据后,要通过调用List的notify方法通知线程B、C、D启动来处理这些数据。
当线程B、C、D成功从List中取出数据后,要通过调用List的notify方法来通知线程A来继续装入数据,如果只有A、List和B,使用wait/notify机制还是很好用的。但是现在有3个线程同时读取List。如果通过调用List的wait和notify。那么结果是,例如B发现List中没有数据。那么它将会调用List的notify方法,那么线程A、C、D都会被陆续的唤醒。CPU的资源很快就被这种死循环吃光了。我该怎么办呢?还望各位高人指点一二。100分全拿出来啦!

解决方案 »

  1.   

    能不能让他们"吃"完东西后sleep一会啊,缓缓劲
      

  2.   

    不能Sleep。最多就是Wait。关键是要在有数据的时候把它们叫醒。没有数据就让它们一直睡。
      

  3.   

    假设A不定期地放入数据,B、C、D线程分别不定期地去数据。先说没有数据的情况:
    以list作为锁,那么当B、C、D发现其中没有数据时,调用list的wait方法;A每次放入数据时,都调用list的notify方法,唤醒B、C、D之一。
    这里的list锁被BCD用来等待新数据。下面说数据个数大于1000的情况:
    由于list锁已经有了特定的含义,不能再赋予它别的含义,否则,会引起混乱!
    需采用另外的对象作为锁,任何对象都可以,比如:
      Object lock2 = new String("");
    当A发现list中数据超过1000时,调用lock2.wait(),BCD每次取出数据时都调用lock2.notify()方法。你的问题主要出在把两个含义的锁混为一谈了,导致混乱。
      

  4.   

    那么线程A、C、D都会被陆续的唤醒。CPU的资源很快就被这种死循环吃光了
    ------------------------------------
    如果出现这样的情况,一定是你的做法不对,你要调用yield或sleep释放一下CPU最简单的方法,把缓存做一个类,ABCD线程分别操作这个类public class Test {
        public static void main(String[] args) {
            try {
                DataBuffer buffer = new DataBuffer();
                Thread a = new LoadDataThread(buffer);
                a.start();
                Thread[] b = new Thread[3];
                for (int i=0; i<b.length; i++) {
                    b[i] = new TakeDataThread(buffer);
                    b[i].start();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }class DataBuffer {
        private List list;
        public DataBuffer() {
            list = new ArrayList();
        } 
        
        public void loadData() throws Exception {
            synchronized (this) {
                while (list.size() >= 1000) wait();
                Object obj = loadDataFromXXX();
                list.add(obj);
                notifyAll();
            }
        }    public Object takeData() throws Exception {
            Object obj;
            synchronized (this) {
                while (list.size() == 0) wait();
                obj = list.remove(0);
                notifyAll();
            }
            return obj;
        }    protected Object loadDataFromXXX () {
            //create or load you data from you divice, for example
            return new Date();
        }
    }class LoadDataThread extends Thread {
        private DataBuffer buffer;
        public LoadDataThread(DataBuffer buffer) {
            this.buffer = buffer;
        }    public void run() {
            try {
                while (true) {
                    buffer.loadData();
                    yield(); //or sleep(100);
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }class TakeDataThread extends Thread {
        private DataBuffer buffer;
        public LoadDataThread(DataBuffer buffer) {
            this.buffer = buffer;
        }    public void run() {
            try {
                while (true) {
                    Object data buffer.takeData();
                    //do something here, for example
                    System.out.println("data=" + data);
                    yield(); // or sleep(100);
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
    直接使用List的方法public class Test {
        public static void main(String[] args) {
            try {
                Object locker = new Object();
                List list = new ArrayList();
                Thread a = new LoadDataThread(list, locker);
                a.start();
                Thread[] b = new Thread[3];
                for (int i=0; i<b.length; i++) {
                    b[i] = new TakeDataThread(list, locker);
                    b[i].start();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }class LoadDataThread extends Thread {
        private List list;
        private Object locker;
        public LoadDataThread(List list, Object locker) {
            this.list = list;
            this.locker = locker;
        }    public void run() {
            try {
                while (true) {
                    synchronized (locker) {
                        while (list.size)() >= 1000) locker.wait();
                        Object data = loadDataFromXXX();
                        list.add(data);
                        locker.notifyAll();
                    }
                    yield(); //or sleep(100);
                } 
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }    protected Object loadDataFromXXX() {
            //create data of load data from your divice, for example
            return new Date();
        }
    }class TakeDataThread extends Thread {
        private List list;
        private Object locker;
        public LoadDataThread(List list, Object locker) {
            this.list = list;
            this.locker = locker;
        }    public void run() {
            try {
                while (true) {
                    synchronized (locker) {
                        while (list.size)() == 0) locker.wait();
                        Object data = list.remove(0);
                        //do something here, for example
                        System.out.println("data=" + data);
                        locker.notifyAll();
                    }
                    yield(); //or sleep(100);
                } 
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }
      

  5.   

    你可以直接使用java.util.concurrent.ArrayBlockingQueue,已经实现你需要的逻辑。
    例如B发现List中没有数据。那么它将会调用List的notify方法,那么线程A、C、D都会被陆续的唤醒。CPU的资源很快就被这种死循环吃光了。这个逻辑是错误的,你应该是当B发现没有数据时,调用wait!它是需要等待A放入后唤醒
    大致逻辑是:A:
    1、取得list的锁;
    2.1、当还有空间时,放入数据对象,调用notify或notifyAll唤醒B、C、D这些正在等待的线程
    2.2、当空间超出限制时,调用wait等待B、C、D取出数据后唤醒自己
    2.3、当被唤醒时,重复2,即如果仍然没有空间则需要再waitB:
    1、取得list的锁
    2.1、当还有数据时,取出一个对象,调用notifyAll唤醒A、C、D
    2.2、当没有数据时,调用wait等待A唤醒自己
    2.3、当被唤醒时,重复2,如果没有数据需要再次wait