现在有这个一个场景:
有一个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分全拿出来啦!
有一个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分全拿出来啦!
以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()方法。你的问题主要出在把两个含义的锁混为一谈了,导致混乱。
------------------------------------
如果出现这样的情况,一定是你的做法不对,你要调用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();
}
}
}
例如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