I use lock and unlock in the my code And start some Customer and Producer Thread.
line lock.waite throws IllegalMonitorStateException.Why?
With the lock, the conditions for using this list are not provided in one Thread?
static class Customeer extends Thread {
private List<String> list;
private Lock lock;
public Customeer(List<String> list, Lock lock) {
this.list = list;
this.lock = lock;
}
#Override
public void run() {
lock.lock();
if (list.size() == 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.remove(0);
lock.unlock();
}
}
static class Producer extends Thread {
private List<String> list;
private Lock lock;
public Producer(List<String> list, Lock lock) {
this.list = list;
this.lock = lock;
}
#Override
public void run() {
lock.lock();
list.add("hello");
list.notify();
lock.unlock();
}
}
You have some problems with your code, namely:
list.wait(); you can't acquire the list's monitor unless you are within a synchronized method (or block code).
list.notify();, you can't release the list's a monitor unless you are within a synchronized method (r block code).
You can't use .wait() or .notify() from none synchronized method or section.
Change your code like that snippet:
static class Customeer extends Thread {
private List<String> list;
private Lock lock;
public Customeer(List<String> list, Lock lock) {
this.list = list;
this.lock = lock;
}
#Override
public void run() {
lock.lock();
if (list.size() != 0) {
list.remove(0);
}
lock.unlock();
}
}
static class Producer extends Thread {
private List<String> list;
private Lock lock;
public Producer(List<String> list, Lock lock) {
this.list = list;
this.lock = lock;
}
#Override
public void run() {
lock.lock();
list.add("hello");
lock.unlock();
}
}
These strings are calling IllegalMonitorStateException.
line lock.wait throws IllegalMonitorStateException. Why?
Actually, there is no such line.
However, there is a line that calls list.wait(). And that is the cause of your problems.
In order to call wait() on an object, you must first be holding the primitive mutex lock on that object. You can only get that kind of lock using synchronized. (Either a synchronized method or a synchronized block.)
In your case:
You are calling wait on the List instance.
You are locking the Lock instance.
You are holding the wrong kind of lock for Object.wait. You are holding a Lock lock, not holding a primitive mutex lock.
So ... if you want to do the equivalent of wait and notify on a Lock instance, then when you need to do is to call Lock.newCondition() to get a Condition object. Then you use it like this:
private final Lock lock = ...
private final Condition cond = lock.newCondition();
try {
lock.acquire();
while (!the_condition_we_are_waiting_for) {
cond.await();
}
// do stuff
} finally {
lock.release();
}
For reference, the above would look like this if you rewrote it to use a primitive mutex.
private final Object lock = new Object();
synchronized(lock) {
while (!the_condition_we_are_waiting_for) {
lock.wait();
}
// do stuff
}
(You could use any object as the lock, but it is a good idea to use an object that is hidden, and won't be locked by any other code.)
In summary, either use a primitive mutex with synchronized, Object.wait and Object.notify* OR use a Lock with Lock.acquire, Lock.release, Condition.await and Condition.signal. Don't try to mix the two kinds of locking and condition variables.
Related
In old synchronized block, we used same object to synchronize on, also used wait and notify methods. So they can all refer to same lock. Makes sense.
So when I use class ReentrantLock, why can't I also use same variable to call lock, unlock as well as await and signal? Why do I need to make additional Condition variable?
That is, why I need to do this:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
void doSomething() {
lock.lock();
//some code
condition.await();
//some code
lock.unlock();
}
Instead of this: (wouldn't this type of coding be more logic)?
Lock lock = new ReentrantLock();
void doSomething() {
lock.lock();
//some code
lock.await();
//some code
lock.unlock();
}
EDIT: from docs: A Condition instance is intrinsically bound to a lock.
Why design it that way? Why not just have one variable of type Lock which would have await and signal method?
The separation of Lock and Condition allows you to have more than one Condition per Lock, which is documented by Condition:
Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object [emphasis added], by combining them with the use of arbitrary Lock implementations.
And Lock:
[Lock implementations] allow more flexible structuring, may have quite different properties, and may support multiple associated Condition objects [emphasis added].
With that ability you can do things like:
import java.util.Objects;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Stack<E> {
private final Lock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private final Object[] elements;
private int size;
public Stack(int capacity) {
elements = new Object[capacity];
}
public E pop() throws InterruptedException {
lock.lockInterruptibly();
try {
while (size == 0) {
notEmpty.await();
}
#SuppressWarnings("unchecked")
E element = (E) elements[--size];
elements[size] = null;
notFull.signal();
return element;
} finally {
lock.unlock();
}
}
public void push(E element) throws InterruptedException {
Objects.requireNonNull(element);
lock.lockInterruptibly();
try {
while (size == elements.length) {
notFull.await();
}
elements[size++] = element;
notEmpty.signal();
} finally {
lock.unlock();
}
}
}
This approach gives two benefits:
When an element is pushed only a thread waiting to pop an element is signaled and vice versa. In other words, only the thread(s) waiting on a specific Condition are signaled.
You don't have to invoke signalAll(), meaning only one thread is woken up.
(Bonus) Improves readability of code, at least in my opinion.
Here's the same Stack class but using synchronized:
import java.util.Objects;
public class Stack<E> {
private final Object lock = new Object();
private final Object[] elements;
private int size;
public Stack(int capacity) {
elements = new Object[capacity];
}
public E pop() throws InterruptedException {
synchronized (lock) {
while (size == 0) {
lock.wait();
}
#SuppressWarnings("unchecked")
E element = (E) elements[--size];
elements[size] = null;
lock.notifyAll();
return element;
}
}
public void push(E element) throws InterruptedException {
Objects.requireNonNull(element);
synchronized (lock) {
while (size == elements.length) {
lock.wait();
}
elements[size++] = element;
lock.notifyAll();
}
}
}
Notice now that every thread has to wait on the same "condition" and that every waiting thread is notified any time anything happens. You have to notify all waiting threads because you have no finer control over which thread(s) are notified.
I have structure something like this:
Lock wrapper - is used to store lock, condition and an object from response
public class LockWrapper{
private Lock lock;
private Condition myCondition;
private MyObject myObject;
public LockWrapper(Lock lock, Condition myCondition) {
this.lock = lock;
this.myCondition = myCondition;
}
public Condition getMyCondition() {
return myCondition;
}
public MyObject getMyObject() {
return myObject;
}
public void setObject(MyObject myObject) {
this.myObject = myObject;
}
public Lock getLock() {
return lock;
}
}
Task - pushed into a thread pool for execution. It initiates requests to a server and then waits for server responses.
public class MyTask implements Runnable{
private Lock lock = new ReentrantLock();
private Condition myCondition = lock.newCondition();
private MyWebSocketAPI api;
public MyTask(MyWebSocketAPI api) {
this.api = api;
}
#Override
public void run() {
lock.lock();
try {
// long randomLong = generateRandomLong();
api.sendRequest(randomLong, new LockWrapper(lock, myCondition));
myCondition.await();
//do something after we got a response
} finally{
lock.unlock();
}
}
}
WebSocket - gets requests and notifies tasks about responses
public abstract class MyWebSocketAPI extends WebSocketClient {
//...
private Map<Long, LockWrapper> lockWrappers = new ConcurrentHashMap<>();
public void sendRequest(Long id, LockWrapper lockWrapper){
this.lockWrappers.put(id, lockWrapper);
//processRequest
}
#Override
public void onMessage(String message) {
LockWrapper lockWrapper = lockWrappers.get(message.get(0).getAsLong());
lockWrapper.getLock().lock();
try{
lockWrapper.setMyObject(new MyObject(message));
this.lockWrappers.put(message.get(0).getAsLong(), lockWrapper);
lockWrapper.getMyCondition().signalAll();
} finally {
lockWrapper.getLock().unlock();
}
}
//...
}
Line lockWrapper.getMyCondition().signalAll(); throws an exception:
java.lang.IllegalMonitorStateException: null
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signalAll(AbstractQueuedSynchronizer.java:1954)
Why my conditions throw this exception when I try to notify tasks that we got their objects? Did I make a mistake somewhere or Java doesn't allow shared conditions?
It was my error in the Task. The problem was that I was creating Lock and Condition both global and local in method run. Lock and condition had the same name. In some cases I was using lock and in some cases this.lock (But it was two different locks). As a result, in method onMessage I had Condition and Lock which were not connected together.
After I removed duplicates everything works.
i want to write own simple semaphore and done it as follows:
class Semaphore {
private boolean done;
private final Object lock = new Object();
public Semaphore(boolean done){ this.done = done;}
public void acquire() throws InterruptedException {
synchronized (lock) {
while (!done)
lock.wait();
done = false;
}
}
public void release() {
synchronized (lock) {
done = true;
lock.notify();
}
}
}
it works fine. But if i replace synchronized (lock) with synchronize (this) it begins to throw IllegalMonitorStateException. Why so?
As #Alexei Kaigorodov mentioned in comment, when you replace synchronized (lock) with synchronize (this). Then, you need to also replace lock to this in your code.
As this indicate to current object which is different than lock object.
Now, you replaced synchronized (lock) with synchronize (this) which means now you are trying to acquire lock on current object but you were waiting on object of Object class.
This works absolutely fine :
public void acquire() throws InterruptedException {
synchronized (this) {
while (!done)
this.wait();
done = false;
}
}
public void release() {
synchronized (this) {
done = true;
this.notify();
}
}
Suppose there is the following code:
class MyClass {
synchronized void myMethod1() {
//code
}
synchronized void myMethod2() {
//code
}
}
Now suppose myMethod1() and myMethod2() access distinct data; now if there are two threads, thread A calling only myMethod1() and thread B calling only myMethod2().
If thread A is executing myMethod1(), will thread B block waiting on myMethod2() even if they don't access the same data and there is no reason for this? As far as I know, synchronized methods use the monitor of this object for instance methods and that of MyClass.class object for static functions.
Your understanding of the situation is correct.
The typical solution is to have separate dedicated lock objects for the resources in question.
class MyClass {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
void myMethod1() {
lock1.lock();
try {
//code
} finally {
lock1.unlock();
}
}
void myMethod2() {
lock2.lock();
try {
//code
} finally {
lock2.unlock();
}
}
}
You are correct in all your suppositions. In the case where no data is in common then there is no reason to synchronize at the method level.
The sychronized method will lock on the object itself. So each method will have to wait for the other to finish its access to release the object. If your methods are truly accessing distinct data you can do something like this:
class MyClass {
private static Object mLock1 = new Object();
private static Object mLock2 = new Object();
void myMethod1() {
synchronized(mLock1) {
//code
}
}
void myMethod2() {
synchronized(mLock2) {
//code
}
}
}
And you can then access them independently.
edit: You can essentially think of synchronized methods as being equivalent to this:
void myMethod1() {
synchronized(this) {
//your code
}
}
Shown like this it is pretty clear why two synchronized methods block each other because they have to wait for the lock on this to free.
Yes, declaring both methods as synchronized will make them block each other, even if they access different data elements.
To avoid this, you can use a more fine grained locks. E.g.:
class MyClass {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
void myMethod1() {
synchronized (lock1) {
//code
}
}
void myMethod2() {
synchronized (lock2) {
//code
}
}
You can use different monitors for myMethod1 and myMethod2 as follows:
class MyClass {
Object monitor1 = new Object();
Object monitor2 = new Object();
void myMethod1() {
synchornized(monitor1) {
//code
}
}
void myMethod2() {
synchronized(monitor2) {
//code
}
}
}
public class ThreadTest
{
public static Integer i = new Integer(0);
public static void main(String[] args) throws InterruptedException
{
ThreadTest threadTest = new ThreadTest();
Runnable odd = threadTest.new Numbers(1, "thread1");
Runnable even = threadTest.new Numbers(0, "thread2");
((Thread) odd).start();
((Thread) even).start();
}
class Numbers extends Thread
{
int reminder;
String threadName;
Numbers(int reminder, String threadName)
{
this.reminder = reminder;
this.threadName = threadName;
}
#Override
public void run()
{
while (i < 20)
{
synchronized (i)
{
if (i % 2 == reminder)
{
System.out.println(threadName + " : " + i);
i++;
i.notify();
}
else
{
try
{
i.wait();
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
}
You can't synchronize on i because it changes during execution of your program.
Since Integer in Java is immutable, after executing i++ i will contain a reference to another object, not the object you have synchronized on. So, you can't call wait()/notify() on this new object, because these methods may be only called on the object you are synchronized on, otherwise you get IllegalMonitorStateException.
You need to synchronize on some other object that doesn't change during execution. For example, you may create a separate object for this purpose:
public class ThreadTest {
public static Integer i = new Integer(0);
public static Object lock = new Object();
...
class Numbers extends Thread {
...
#Override
public void run() {
...
synchronized (lock) {
...
lock.notify();
...
lock.wait();
...
}
}
}
}
This line:
i++;
is equivalent to:
i = i + 1;
which (due to autoboxing) becomes something like:
i = new Integer(i.intValue() + 1);
So, when you call i.notify() you are synchronized on the old i, not the new one.
I'd suggest changing i into an ordinary int variable, and create a separate object to synchronize on:
static int i = 0;
static Object iMonitor = new Object();
As documentation states the exception is thrown when
the current thread is not the owner of the object's monitor
It also states that
This method should only be called by a thread that is the owner of this object's monitor.
And this condition can be obtained by
By executing a synchronized instance method of that object.
By executing the body of a synchronized statement that synchronizes on the object.
For objects of type Class, by executing a synchronized static method of that class.
You could try calling the wait method from inside the class that uses i. This could be done by extending the class and writing two new methods for notify and wait..
You cannot put wait() and notify() in the same synchronized block because that will just cause a deadlock. Make sure only the wait and notify functions are wrapped with a synchronized block like this:
synchronized (i) {
i.wait(); // or i.notify();
}