I am trying to create a solution for Producer/ Consumer problem where one thread is putting message in Vector and another is removing from it.
import java.util.Vector;
public class Producer implements Runnable {
static final int MAXQUEUE = 5;
private Vector<String> messages;
public Producer(Vector<String> messages) {
super();
this.messages = messages;
}
#Override
public void run() {
try {
while (true)
putMessage();
} catch (InterruptedException e) {
}
}
private synchronized void putMessage() throws InterruptedException {
while (messages.size() == MAXQUEUE) {
wait();
}
messages.addElement(new java.util.Date().toString());
System.out.println("put message");
notifyAll();
}
public static void main(String args[]) {
Vector<String> messages = new Vector<String>();
new Thread(new Producer(messages)).start();
new Thread(new Consumer(messages)).start();
}
}
class Consumer implements Runnable{
public Consumer(Vector<String> messages) {
super();
this.messages = messages;
}
private Vector<String> messages;
public synchronized String getMessage() throws InterruptedException {
notifyAll();
while (messages.size() == 0) {
wait();//By executing wait() from a synchronized block, a thread gives up its hold on the lock and goes to sleep.
}
String message = (String) messages.firstElement();
messages.removeElement(message);
return message;
}
#Override
public void run() {
try {
while (true) {
String message = getMessage();
System.out.println("Got message: " + message);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Whenever I am running the program, it is printing put message 5 times. I don't understand even after notifyAll(), it is not giving lock to consumer.
Your code is not working because your two threads are not notifying/waiting on the same monitor.
They each notify and wait on their own monitor, not a shared monitor. Change code to use a shared monitor, e.g. messages, including the synchronizations.
private void putMessage() throws InterruptedException {
synchronized (messages) { // <======
while (messages.size() == MAXQUEUE) {
messages.wait(); // <======
}
messages.addElement(new java.util.Date().toString());
System.out.println("put message");
messages.notifyAll(); // <======
}
}
public String getMessage() throws InterruptedException {
synchronized (messages) { // <======
while (messages.size() == 0) {
messages.wait(); // <======
}
String message = (String) messages.firstElement();
messages.removeElement(message);
messages.notifyAll(); // <======
return message;
}
}
Notice that methods are no longer synchronized.
Logging to the console is very slow so if you do this without holding the lock you give the consumer a chance.
#Override
public void run() {
try {
while (true) {
// slows the producer a little to give the consumer a chance to get the lock.
System.out.println("put message");
putMessage();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void putMessage() throws InterruptedException {
synchronized (messages) {
while (messages.size() == MAXQUEUE) {
messages.wait();
}
messages.addElement(new java.util.Date().toString());
messages.notifyAll();
}
}
BTW on the consumer you can write this
public String getMessage() throws InterruptedException {
synchronized (messages) {
while (messages.isEmpty()) {
messages.wait();//By executing wait() from a synchronized block, a thread gives up its hold on the lock and goes to sleep.
}
messages.notifyAll();
return messages.remove(0);
}
}
Related
I write a Java program to solve Producer Consumer problem in Multi-Threads. But it can not work correctly.
The program:
public class ConsumerAndProducer {
static int products = 0;
static int capacity = 10;
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
static class Consumer implements Runnable{
public void consume() {
synchronized (ConsumerAndProducer.class){
if(products <= 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Consumer, remain:" + products);
if(products == 9){
notify();
}
}
}
#Override
public void run() {
while(true){
consume();
}
}
}
static class Producer implements Runnable{
public void produce() {
synchronized (ConsumerAndProducer.class){
if(products == capacity){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Producer, remain:" + products);
if(products == 1){
notify();
}
}
}
#Override
public void run() {
while(true){
produce();
}
}
}
And the errors:
Producer, remain:1
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at zhousai.ConsumerAndProducer$Producer.produce(ConsumerAndProducer.java:69)
at zhousai.ConsumerAndProducer$Producer.run(ConsumerAndProducer.java:77)
at java.lang.Thread.run(Thread.java:748)
Consumer, remain:0
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:502)
at zhousai.ConsumerAndProducer$Consumer.consume(ConsumerAndProducer.java:22)
at zhousai.ConsumerAndProducer$Consumer.run(ConsumerAndProducer.java:43)
at java.lang.Thread.run(Thread.java:748)
When I ran your code, I got the following error:
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException: current thread is not owner
The line of your code throwing that exception is the call to method wait().
You are calling method wait() of class Producer but you are synchronizing on ConsumerAndProducer.class. The wait() method must be called on the object that you are synchronizing on, because that object owns the lock and you must call wait() on the object that owns the lock. Hence the error message: current thread not owner.
The simplest solution is to change your code such that you call ConsumerAndProducer.class.wait() rather than just wait().
Here is your code with my suggested fix:
public class ConsumerAndProducer {
static int products = 0;
static int capacity = 10;
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
static class Consumer implements Runnable {
public void consume() {
synchronized (ConsumerAndProducer.class){
if (products <= 0) {
try {
ConsumerAndProducer.class.wait(); // change here
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Consumer, remain:" + products);
if(products == 9){
ConsumerAndProducer.class.notify(); // change here
}
}
}
#Override
public void run() {
while(true){
consume();
}
}
}
static class Producer implements Runnable{
public void produce() {
synchronized (ConsumerAndProducer.class){
if (products == capacity) {
try {
ConsumerAndProducer.class.wait(); // change here
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Producer, remain:" + products);
if(products == 1){
ConsumerAndProducer.class.notify(); // change here
}
}
}
#Override
public void run() {
while(true){
produce();
}
}
}
}
This question already has an answer here:
Why Java throw java.lang.IllegalMonitorStateException when I invoke wait() in static way synchronized block?
(1 answer)
Closed 2 years ago.
In the below code for producer and consumer, I thought that the produce() and consume() methods are synchronized on Class Lock (Processor.class), but i am getting an exception stating IllegalMonitorStateException, which occurs for objects on which we don't acquire lock but we notify on that objects.
Can anyone tell me where i have gone wrong in the program.
package ProducerConsumer;
public class Main {
public static void main(String[] args) {
Processor processor = new Processor();
Thread producer = new Thread(new Runnable() {
public void run() {
try {
processor.produce();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread consumer = new Thread(new Runnable() {
public void run() {
try {
processor.consume();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("\t\t\tStarting both producer and consumer Threads.");
producer.start();
consumer.start();
try {
producer.join();
consumer.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\t\t\tEnding all the Threads.");
}
}
import java.util.List;
import java.util.ArrayList;
public class Processor {
private List<Integer> list = new ArrayList<>();
private int value = 0;
private final int LIMIT = 5;
public void produce() throws InterruptedException
{
synchronized(Processor.class){
while(true)
{
if(list.size() == LIMIT){
System.out.println("Waiting for consumer to consume resources");
wait();
}
else{
value++;
System.out.println("The produced resource is : "+value);
list.add(value);
notify();
}
}
}
}
public void consume() throws InterruptedException
{
synchronized(Processor.class){
while(true)
{
if(list.isEmpty()){
System.out.println("Waiting for producer to produce the resources");
wait();
}
else{
System.out.println("The consumer Consumed Resource is : "+list.remove(0));
notify();
}
}
}
}
}
Your wait() & notify() are invoked on this i.e. Processor processor = new Processor(); but your are locking/synchronizing on Processor.class object. You can fix your code to work as below.
class Processor {
private List<Integer> list = new ArrayList<>();
private int value = 0;
private final int LIMIT = 5;
public void produce() throws InterruptedException
{
synchronized(Processor.class){
while(true)
{
if(list.size() == LIMIT){
System.out.println("Waiting for consumer to consume resources");
Processor.class.wait();
}
else{
value++;
System.out.println("The produced resource is : "+value);
list.add(value);
Processor.class.notify();
}
}
}
}
public void consume() throws InterruptedException
{
synchronized(Processor.class){
while(true)
{
if(list.isEmpty()){
System.out.println("Waiting for producer to produce the resources");
Processor.class.wait();
}
else{
System.out.println("The consumer Consumed Resource is : "+list.remove(0));
Processor.class.notifyAll();
}
}
}
}
}
Problem statement
I have a JMS listener running as a thread listening to a topic. As soon a message comes in, I spawn a new Thread to process the in-bounded message. So for each incoming message I spawn a new Thread.
I have a scenario where duplicate message is also being processed when it is injected immediately in a sequential order. I need to prevent this from being processed. I tried using a ConcurrentHashMap to hold the process times where I add in the entry as soon as Thread is spawn and remove it from the map as soon Thread completes its execution. But it did not help when I tried with the scenario where I passed in same one after the another in concurrent fashion.
General Outline of my issue before you plunge into the actual code base
onMessage(){
processIncomingMessage(){
ExecutorService executorService = Executors.newFixedThreadPool(1000);
//Map is used to make an entry before i spawn a new thread to process incoming message
//Map contains "Key as the incoming message" and "value as boolean"
//check map for duplicate check
//The below check is failing and allowing duplicate messages to be processed in parallel
if(entryisPresentInMap){
//return doing nothing
}else{
//spawn a new thread for each incoming message
//also ensure a duplicate message being processed when it in process by an active thread
executorService.execute(new Runnable() {
#Override
public void run() {
try {
//actuall business logic
}finally{
//remove entry from the map so after processing is done with the message
}
}
}
}
Standalone example to mimic the scenario
public class DuplicateCheck {
private static Map<String,Boolean> duplicateCheckMap =
new ConcurrentHashMap<String,Boolean>(1000);
private static String name=null;
private static String[] nameArray = new String[20];
public static void processMessage(String message){
System.out.println("Processed message =" +message);
}
public static void main(String args[]){
nameArray[0] = "Peter";
nameArray[1] = "Peter";
nameArray[2] = "Adam";
for(int i=0;i<=nameArray.length;i++){
name=nameArray[i];
if(duplicateCheckMap.get(name)!=null && duplicateCheckMap.get(name)){
System.out.println("Thread detected for processing your name ="+name);
return;
}
addNameIntoMap(name);
new Thread(new Runnable() {
#Override
public void run() {
try {
processMessage(name);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
freeNameFromMap(name);
}
}
}).start();
}
}
private static synchronized void addNameIntoMap(String name) {
if (name != null) {
duplicateCheckMap.put(name, true);
System.out.println("Thread processing the "+name+" is added to the status map");
}
}
private static synchronized void freeNameFromMap(String name) {
if (name != null) {
duplicateCheckMap.remove(name);
System.out.println("Thread processing the "+name+" is released from the status map");
}
}
Snippet of the code is below
public void processControlMessage(final Message message) {
RDPWorkflowControlMessage rdpWorkflowControlMessage= unmarshallControlMessage(message);
final String workflowName = rdpWorkflowControlMessage.getWorkflowName();
final String controlMessageEvent=rdpWorkflowControlMessage.getControlMessage().value();
if(controlMessageStateMap.get(workflowName)!=null && controlMessageStateMap.get(workflowName)){
log.info("Cache cleanup for the workflow :"+workflowName+" is already in progress");
return;
}else {
log.info("doing nothing");
}
Semaphore controlMessageLock = new Semaphore(1);
try{
controlMessageLock.acquire();
synchronized(this){
new Thread(new Runnable(){
#Override
public void run() {
try {
lock.lock();
log.info("Processing Workflow Control Message for the workflow :"+workflowName);
if (message instanceof TextMessage) {
if ("REFRESH".equalsIgnoreCase(controlMessageEvent)) {
clearControlMessageBuffer();
enableControlMessageStatus(workflowName);
List<String> matchingValues=new ArrayList<String>();
matchingValues.add(workflowName);
ConcreteSetDAO tasksSetDAO=taskEventListener.getConcreteSetDAO();
ConcreteSetDAO workflowSetDAO=workflowEventListener.getConcreteSetDAO();
tasksSetDAO.deleteMatchingRecords(matchingValues);
workflowSetDAO.deleteMatchingRecords(matchingValues);
fetchNewWorkflowItems();
addShutdownHook(workflowName);
}
}
} catch (Exception e) {
log.error("Error extracting item of type RDPWorkflowControlMessage from message "
+ message);
} finally {
disableControlMessageStatus(workflowName);
lock.unlock();
}
}
}).start();
}
} catch (InterruptedException ie) {
log.info("Interrupted Exception during control message lock acquisition"+ie);
}finally{
controlMessageLock.release();
}
}
private void addShutdownHook(final String workflowName) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
disableControlMessageStatus(workflowName);
}
});
log.info("Shut Down Hook Attached for the thread processing the workflow :"+workflowName);
}
private RDPWorkflowControlMessage unmarshallControlMessage(Message message) {
RDPWorkflowControlMessage rdpWorkflowControlMessage = null;
try {
TextMessage textMessage = (TextMessage) message;
rdpWorkflowControlMessage = marshaller.unmarshalItem(textMessage.getText(), RDPWorkflowControlMessage.class);
} catch (Exception e) {
log.error("Error extracting item of type RDPWorkflowTask from message "
+ message);
}
return rdpWorkflowControlMessage;
}
private void fetchNewWorkflowItems() {
initSSL();
List<RDPWorkflowTask> allTasks=initAllTasks();
taskEventListener.addRDPWorkflowTasks(allTasks);
workflowEventListener.updateWorkflowStatus(allTasks);
}
private void clearControlMessageBuffer() {
taskEventListener.getRecordsForUpdate().clear();
workflowEventListener.getRecordsForUpdate().clear();
}
private synchronized void enableControlMessageStatus(String workflowName) {
if (workflowName != null) {
controlMessageStateMap.put(workflowName, true);
log.info("Thread processing the "+workflowName+" is added to the status map");
}
}
private synchronized void disableControlMessageStatus(String workflowName) {
if (workflowName != null) {
controlMessageStateMap.remove(workflowName);
log.info("Thread processing the "+workflowName+" is released from the status map");
}
}
I have modified my code to incorporate suggestions provided below but still it is not working
public void processControlMessage(final Message message) {
ExecutorService executorService = Executors.newFixedThreadPool(1000);
try{
lock.lock();
RDPWorkflowControlMessage rdpWorkflowControlMessage= unmarshallControlMessage(message);
final String workflowName = rdpWorkflowControlMessage.getWorkflowName();
final String controlMessageEvent=rdpWorkflowControlMessage.getControlMessage().value();
if(controlMessageStateMap.get(workflowName)!=null && controlMessageStateMap.get(workflowName)){
log.info("Cache cleanup for the workflow :"+workflowName+" is already in progress");
return;
}else {
log.info("doing nothing");
}
enableControlMessageStatus(workflowName);
executorService.execute(new Runnable() {
#Override
public void run() {
try {
//actual code
fetchNewWorkflowItems();
addShutdownHook(workflowName);
}
}
} catch (Exception e) {
log.error("Error extracting item of type RDPWorkflowControlMessage from message "
+ message);
} finally {
disableControlMessageStatus(workflowName);
}
}
});
} finally {
executorService.shutdown();
lock.unlock();
}
}
private void addShutdownHook(final String workflowName) {
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
disableControlMessageStatus(workflowName);
}
});
log.info("Shut Down Hook Attached for the thread processing the workflow :"+workflowName);
}
private synchronized void enableControlMessageStatus(String workflowName) {
if (workflowName != null) {
controlMessageStateMap.put(workflowName, true);
log.info("Thread processing the "+workflowName+" is added to the status map");
}
}
private synchronized void disableControlMessageStatus(String workflowName) {
if (workflowName != null) {
controlMessageStateMap.remove(workflowName);
log.info("Thread processing the "+workflowName+" is released from the status map");
}
}
This is how you should add a value to a map. This double checking makes sure that only one thread adds a value to a map at any particular moment of time and you can control the access afterwards. Remove all the locking logic afterwards. It is as simple as that
public void processControlMessage(final String workflowName) {
if(!tryAddingMessageInProcessingMap(workflowName)){
Thread.sleep(1000); // sleep 1 sec and try again
processControlMessage(workflowName);
return ;
}
System.out.println(workflowName);
try{
// your code goes here
} finally{
controlMessageStateMap.remove(workflowName);
}
}
private boolean tryAddingMessageInProcessingMap(final String workflowName) {
if(controlMessageStateMap .get(workflowName)==null){
synchronized (this) {
if(controlMessageStateMap .get(workflowName)==null){
controlMessageStateMap.put(workflowName, true);
return true;
}
}
}
return false;
}
Read here more for https://en.wikipedia.org/wiki/Double-checked_locking
The issue is fixed now. Many thanks to #awsome for the approach. It is avoiding the duplicates when a thread is already processing the incoming duplicate message. If no thread is processing then it gets picked up
public void processControlMessage(final Message message) {
try {
lock.lock();
RDPWorkflowControlMessage rdpWorkflowControlMessage = unmarshallControlMessage(message);
final String workflowName = rdpWorkflowControlMessage.getWorkflowName();
final String controlMessageEvent = rdpWorkflowControlMessage.getControlMessage().value();
new Thread(new Runnable() {
#Override
public void run() {
try {
if (message instanceof TextMessage) {
if ("REFRESH".equalsIgnoreCase(controlMessageEvent)) {
if (tryAddingWorkflowNameInStatusMap(workflowName)) {
log.info("Processing Workflow Control Message for the workflow :"+ workflowName);
addShutdownHook(workflowName);
clearControlMessageBuffer();
List<String> matchingValues = new ArrayList<String>();
matchingValues.add(workflowName);
ConcreteSetDAO tasksSetDAO = taskEventListener.getConcreteSetDAO();
ConcreteSetDAO workflowSetDAO = workflowEventListener.getConcreteSetDAO();
tasksSetDAO.deleteMatchingRecords(matchingValues);
workflowSetDAO.deleteMatchingRecords(matchingValues);
List<RDPWorkflowTask> allTasks=fetchNewWorkflowItems(workflowName);
updateTasksAndWorkflowSet(allTasks);
removeWorkflowNameFromProcessingMap(workflowName);
} else {
log.info("Cache clean up is already in progress for the workflow ="+ workflowName);
return;
}
}
}
} catch (Exception e) {
log.error("Error extracting item of type RDPWorkflowControlMessage from message "
+ message);
}
}
}).start();
} finally {
lock.unlock();
}
}
private boolean tryAddingWorkflowNameInStatusMap(final String workflowName) {
if(controlMessageStateMap.get(workflowName)==null){
synchronized (this) {
if(controlMessageStateMap.get(workflowName)==null){
log.info("Adding an entry in to the map for the workflow ="+workflowName);
controlMessageStateMap.put(workflowName, true);
return true;
}
}
}
return false;
}
private synchronized void removeWorkflowNameFromProcessingMap(String workflowName) {
if (workflowName != null
&& (controlMessageStateMap.get(workflowName) != null && controlMessageStateMap
.get(workflowName))) {
controlMessageStateMap.remove(workflowName);
log.info("Thread processing the " + workflowName+ " is released from the status map");
}
}
According to wikipedia: "The race conditions that cause spurious wakeups should be considered rare".
But when I run this code, it is showing me that spurious wakeup happens quite often.
Is this actually spurious wakeup or there's just a sneaky race condition in my code?
import java.util.Random;
public class Main {
public static void main(String[] args) throws Exception {
// Message message = new SafeMessage();
Message message = new SpuriousMessage();
String[] producerNames = { "p01", "p02", "p03", "p04", "p05", "p06", "p07", "p08", "p09" };
for (String producerName : producerNames) {
Producer producer = new Producer(producerName, message);
new Thread(producer).start();
}
String[] consumerNames = { "c-01", "c-02", "c-03", "c-04" };
for (String consumerName : consumerNames) {
Consumer consumer = new Consumer(consumerName, message);
new Thread(consumer).start();
}
}
}
abstract class Message {
protected String message;
protected boolean empty = true;
public abstract String getMessage() throws InterruptedException;
public abstract void setMessage(String message) throws InterruptedException;
protected static String avoidNull(String obj) {
return obj != null ? obj : "Default message";
}
}
class SpuriousMessage extends Message {
#Override
public synchronized String getMessage() throws InterruptedException {
wait();
empty = true;
String temp = message;
message = "---------------------------------------- Spurious wakeup";
return temp;
}
#Override
public synchronized void setMessage(String message) throws InterruptedException {
this.message = avoidNull(message);
this.empty = false;
notifyAll();
}
}
class SafeMessage extends Message {
#Override
public synchronized String getMessage() throws InterruptedException {
while (empty) {
wait();
}
empty = true;
notifyAll();
String temp = message;
message = "---------------------------------------- Spurious wakeup";
return temp;
}
#Override
public synchronized void setMessage(String message) throws InterruptedException {
while (!empty) {
wait();
}
this.message = avoidNull(message);
this.empty = false;
notifyAll();
}
}
class Producer implements Runnable {
private static final Random RANDOM = new Random();
private String producerName = "Default";
private Message message;
public Producer(String producerName, Message message) {
this.producerName = producerName;
this.message = message;
}
#Override
public void run() {
while (true) {
try {
message.setMessage(producerName + " :: " + randomMessage());
rest(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static String randomMessage() {
final String[] messageArray = { "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot",
"Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar",
"Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey",
"Xray", "Yankee", "Zulu" };
return messageArray[RANDOM.nextInt(messageArray.length)];
}
private void rest(long millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
private final long TIMEOUT = 5;
private String consumerName = "Default";
private Message message;
public Consumer(String consumerName, Message message) {
this.consumerName = consumerName;
this.message = message;
}
#Override
public void run() {
while (true) {
try {
System.out.println(consumerName + " :: " + message.getMessage());
rest(TIMEOUT);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void rest(long millis) {
try {
Thread.sleep(millis);
} catch (Exception e) {
e.printStackTrace();
}
}
}
When a spurious wakeup happens, wait() exits although notify/notifyAll has not been called. In your case you call notifyAll from the producer so it is normal that wait exits...
To observe a spurious wakeup, you would need to run your Consumers only. If they print the "spurious wakeup" message then that will be a real spurious wakeup because it won't be caused by notify/All any more. However it may never happen.
See also: Do spurious wakeups actually happen?.
I try to understand java core synchronization.
I wrote code sample:
Program should write
left
right
10 times
package concurrency;
public class LeftRightWaitNotifyExample {
final static String str = "1";
public static void main(String[] args) {
new LeftLegThread(str).start();
new RightLegThread(str).start();
}
}
class LeftLegThread extends Thread {
String monitor;
public LeftLegThread(String str) {
monitor = str;
}
#Override
public void run() {
try {
makeStep();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void makeStep() throws InterruptedException {
synchronized (monitor) {
for (int i = 0; i < 10; i++) {
System.out.println("Left ");
wait();
}
}
}
}
class RightLegThread extends Thread {
String monitor;
public RightLegThread(String str) {
monitor = str;
}
#Override
public void run() {
try {
makeStep();
} catch (InterruptedException e) {
}
}
private void makeStep() throws InterruptedException {
synchronized (monitor) {
while (true) {
System.out.println("Right ");
notify();
wait();
}
}
}
}
I get this output:
Left
Right
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at concurrency.LeftLegThread.makeStep(LeftRightWaitNotifyExample.java:35)
at concurrency.LeftLegThread.run(LeftRightWaitNotifyExample.java:23)
Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at concurrency.RightLegThread.makeStep(LeftRightWaitNotifyExample.java:61)
at concurrency.RightLegThread.run(LeftRightWaitNotifyExample.java:51)
Before I got this error when I used wait method non within synchronized block. But here I use wait within synchronized block
What is the cause of the problem and how to fix it?
update
I rewrite code according advice:
public class LeftRightWaitNotifyExample {
final static String str = "1";
public static void main(String[] args) throws InterruptedException {
new LeftLegThread(str).start();
Thread.sleep(100);
new RightLegThread(str).start();
}
}
class LeftLegThread extends Thread {
String monitor;
public LeftLegThread(String str) {
monitor = str;
}
#Override
public void run() {
try {
makeStep();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void makeStep() throws InterruptedException {
synchronized (monitor) {
for (int i = 0; i < 2; i++) {
System.out.println("Left ");
monitor.wait();
monitor.notify();
}
}
}
}
class RightLegThread extends Thread {
String monitor;
public RightLegThread(String str) {
monitor = str;
}
#Override
public void run() {
try {
makeStep();
} catch (InterruptedException e) {
}
}
private void makeStep() throws InterruptedException {
synchronized (monitor) {
while (true) {
System.out.println("Right ");
monitor.notify();
monitor.wait();
}
}
}
}
current output:
Left
Right
Left
Right
Right
Why does Right outs 3 but Left only twice. Why?
You are synchronizing on monitor, so you should wait() on monitor, too:
monitor.wait();
Right now you are waiting on this, which is not the owner of the monitor because synchronization is on monitor.
Note that of course the notify should also be done on the monitor object, and that you might want to consider using notify/notifyAll in both threads. Otherwise it may happen that one thread starves waiting for a missing notification. Using a timeout (the overloaded version of wait) might also be a good idea to catch corner cases.
The reason - The current thread is not the owner of the object's monitor.To call wait() method the current thread must own this object's monitor.
In your case you are obtaining monitor on monitor object instead current object(this object).
you are trying to lock monitor object.But it is locking thread object (LeftLegThread,RightLegThread).Actually it is not locked with synchronization.
monitor.wait(); will fix.
public class LeftRightWaitNotifyExample {
final static String str = "1";
public static void main(String[] args) throws InterruptedException {
new LeftLegThread(str).start();
Thread.sleep(1000);
new RightLegThread(str).start();
}
}
class LeftLegThread extends Thread {
String monitor;
public LeftLegThread(String str) {
monitor = str;
}
#Override
public void run() {
try {
makeStep();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeStep() throws InterruptedException {
synchronized (monitor) {
while (true) {
System.out.println("Left ");
monitor.wait();
monitor.notify();
Thread.sleep(1000);
}
}
}
}
class RightLegThread extends Thread {
String monitor;
public RightLegThread(String str) {
monitor = str;
}
#Override
public void run() {
try {
makeStep();
} catch (InterruptedException e) {
}
}
private void makeStep() throws InterruptedException {
synchronized (monitor) {
while (true) {
System.out.println("Right ");
monitor.notify();
monitor.wait();
Thread.sleep(1000);
}
}
}
}