I found this example from SO. Now, I was trying to understand the usage of wait()
and notify()/notifyAll()
. In which scenario and why do we need this.
class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while (queue.size() == capacity) {
System.out.println("Waiting...");
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while (queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
So, implemented Runnable
and overriden run()
method like below
@Override
public void run() {
// synchronized (this) {
BlockingQueue<Integer> s = new BlockingQueue(10);
for (int i = 0; i < 12; i++) {
try {
s.put(i);
if (i > 9) {
System.out.println(Thread.currentThread().getName() + " : " + s.take());
}
System.out.println(Thread.currentThread().getName() + " ExtendsThread : Counter : " + i);
} //}
//notify();
catch (InterruptedException ex) {
Logger.getLogger(ExtendsThread.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
And, running the thread like below
ImplementsRunnable rc = new ImplementsRunnable();
Thread t1 = new Thread(rc, "A");
t1.start();
When I'm running it, then it is stuck after counter : 9
and keep on waiting for forever. Anyone suggest me what's wrong here ?
Your concept is slightly flawed. The BlockingQueue
can act as bridge in a producer/consumer pattern.
That is, it allows one thread to write content to it and another thread to read content from it, but it's doing it in such away that if:
In this case, the wait
and notify
are internal messaging for the instance of the BlockingQueue
You can have a look at Intrinsic Locks and Synchronization .
So, instead of using just one thread, you should be using (at least) two, a produce and a consumer...
This takes an instance of BlockingQueue
and adds int
values to it. Each time it stops for 1 second before adding the next
public class Producer implements Runnable {
private BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
for (int index = 0; index < 10; index++) {
try {
System.out.println("Put " + index);
queue.put(index);
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
}
}
}
The consumer takes a BlockQueue
and reads int
values from it, which be blocked until a value exists.
public class Consumer implements Runnable {
private BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
Integer value = queue.take();
System.out.println("Took " + value);
}
} catch (InterruptedException ex) {
Logger.getLogger(JavaApplication220.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
You can get these start using something like...
BlockingQueue bq = new BlockingQueue(10);
Thread p = new Thread(new Producer(bq));
Thread c = new Thread(new Consumer(bq));
c.setDaemon(true);
c.start();
p.start();
You should note that there is a small delay between the put
messages, but almost no delay between the took
messages. This is the queue in action. The Consumer
is blocking/waiting on the queue to have something to give it.
You can play around with the Producer
and Consumer
, maybe changing their times (having a longer delay in the Consumer
before taking an item for example) to see how this might cause different effects
When I'm running it, then it is stuck after counter : 9 and keep on waiting for forever
This is likely because you've exceeded the capacity of the queue and it's put
method is blocking until you take something from it (you essentially have a dead lock, where the queue is waiting for you to take something from it, but you can't because you're locked on the put
)
Things to remember:
BlockingQueue
notify
will wake one object whose is waiting on the same instance of the monitor lock's wait
method. There is no way to know which one. This can be useful if you have multiple consumers, but don't care about the order in which the data is processed, for example Updated with additional example
So, this takes the Thread.sleep
out of the Producer
(and allows the producer to produce 100 values) and adds a Thread.sleep
to the Consumer
.
This way, the Producer
will reach it's capacity before the Consumer
can drain it, forcing it to wait until the Consumer
can take values from it...
public class Producer implements Runnable {
private BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
for (int index = 0; index < 100; index++) {
try {
System.out.println("Put " + index);
queue.put(index);
} catch (InterruptedException ex) {
}
}
}
}
public class Consumer implements Runnable {
private BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
@Override
public void run() {
try {
while (true) {
Integer value = queue.take();
System.out.println("Took " + value);
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
}
}
}
Add printlns here
public synchronized void put(T element) throws InterruptedException {
while (queue.size() == capacity) {
System.out.println("blocked");
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
System.out.println("put "+ element);
}
public synchronized T take() throws InterruptedException {
while (queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
System.out.println("removed " + item);
return item;
}
and run this test
public static void main(String argv[]) throws Exception {
final BlockingQueue q = new BlockingQueue(2);
new Thread() {
public void run() {
try {
Thread.sleep(5000);
q.take();
} catch (Exception e) {
e.printStackTrace();
}
};
}.start();
q.put(1);
q.put(2); // will block here until tread 2 takes an element and reduces the capacity
q.put(3);
}
it will print
put 1
put 2
blocked
removed 1
put 3
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.