I am wondering if there is any class to do this, or perhaps a way to Lock a Object by a specific thread (I see there is a method holdsLock(Object obj) that checks if an object is locked by a thread, how do you lock it??) Any help appreciated.
Read the Java docs on multi-threading. Object.wait(), Object.notify() are the APIs you want, along with the synchronize keyword.
Check out the “new” 1.5 (well not really new) concurrent package (jsr-166), there you can find a lot information about threading + java. Great stuff! ;D
http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html
But if all he wants is a mutex/semaphore he doiesn’t need all the new 1.5 stuff.
In Java EVERY object has a Mutext inherently associated with it.
(As others have said see Object.wait() and Object.notify() )
Alright… My problem is that a HashMap isn´t synchronized… And I don´t really get the javadoc of hashMap it tells me to wrap it in some way, but wrapping it in a List class doesn´t make sense… Someone done this before?
There is the code that I want to use a mutex with:
private synchronized void loopWriteQeue() {
while(!writeQeueAvailable) {
try { writeQeue.wait(); }
catch(Exception e) { }
}
writeQeueAvailable = false;
for(Iterator i = writeQeue.keySet().iterator() ; i.hasNext();) {
SpeedChannel key = ((SpeedChannel)i.next());
try { ((SpeedChannel)(writeQeue.get(key))).write(); }
catch(Exception e) { }
if(!((SpeedChannel)writeQeue.get(key)).wantsToWrite()) writeQeue.remove(key);
}
writeQeueAvailable = true;
writeQeue.notify();
}
protected synchronized void addToWriteQeue(SpeedChannel add) {
while(!writeQeueAvailable) {
try { writeQeue.wait(); }
catch(Exception e) { }
}
writeQeueAvailable = false;
writeQeue.put(add, add);
writeQeueAvailable = true;
writeQeue.notify();
}
The solution is right there in the docs:
Map m = Collections.synchronizedMap(new HashMap(...));
But seriously read the Java docs about synchronization, it may be that simply wrapping the map has too fine a granualrity. I find that most of the synchronized collections in the Java API are useless because the synchronization only protects the data structure itself from corruption, but does not protect your algorithm that uses it from corruption.
From Collections.synchronizedMap(Map m) javadoc:
It is imperative that the user manually synchronize on the returned map when iterating over any of its collection views:
Map m = Collections.synchronizedMap(new HashMap());
...
Set s = m.keySet(); // Needn't be in synchronized block
...
synchronized(m) { // Synchronizing on m, not s!
Iterator i = s.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
}
So you would have to synchronize on the synchronized Map when you iterated over the queue.
If you would like to synchronize the queue yourself, this is what the code might look like:
private void loopWriteQeue() {
synchronized(writeQueue) {
for(Iterator i = writeQeue.keySet().iterator() ; i.hasNext();) {
SpeedChannel key = ((SpeedChannel)i.next());
try { ((SpeedChannel)(writeQeue.get(key))).write(); }
catch(Exception e) { }
if(!((SpeedChannel)writeQeue.get(key)).wantsToWrite()) writeQeue.remove(key);
}
}
}
protected void addToWriteQeue(SpeedChannel add) {
synchronized(writeQueue) {
writeQeue.put(add, add);
}
}
It basicly means that only on block of code that is synchronized on the queue is allowed to run at any one time.
Ok guys thanks a whole bunch I think that should solve it! Lets see opens up netbeans
I did what you said (and I understood why I did this) but I still get that damn exception. Could someone explain why? Or give me a hint?
The code:
private void loopWriteQeue() {
Map m = Collections.synchronizedMap(writeQeue);
Set s = m.keySet();
synchronized(m) {
Iterator i = s.iterator();
while(i.hasNext()) {
SpeedChannel key = ((SpeedChannel)i.next());
try { ((SpeedChannel)(writeQeue.get(key))).write(); }
catch(Exception e) { }
if(!((SpeedChannel)writeQeue.get(key)).wantsToWrite()) writeQeue.remove(key);
}
}
}
protected void addToWriteQeue(SpeedChannel add) {
Map m = Collections.synchronizedMap(writeQeue);
synchronized(m) {
writeQeue.put(add, add);
}
}
What exception are you getting?
As each method is obtaining a synchronised Map object individually, they are each getting a different synchronised view of the data. Thus the internal synchronisation to maintain valid state, and the external synchronisation to maintain “transactional” state will be irrelevant. What you want to do is create a shared Map reference (probably a private member variable) that points to the synchronised map, then in each of the methods synchronise on it before calling any of its methods.
Alternatively, if those two methods are the only places where that collection is used, you can forget about getting a synchronised map and just define those two methods as “synchronized”. But that’s what your example code did - wasn’t that working?
I get a concurrency exception on the i.next() method. And yes Ive tried all the ways you described there. I even tried to synchronize on the THIS reference. It seems that it doesnt matter what I synchronize on it will still give me the exception. Here is the full exception:
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at SpeedConn.SpeedConn.loopWriteQeue(SpeedConn.java:97)
at SpeedConn.SpeedConn.run(SpeedConn.java:47)
If you’re getting that, then your synchronisation must be broken somewhere. Make sure that all code using the data is both accessing it via the synchronised reference and passing that reference when entering the synchronized block. Make sure these restrictions will hold for all uses of the data, across different methods, different classes and different threads.
Something somewhere isn’t playing by the rules!
Thats the weird part, I dont touch it anywhere else
Any other ideas or should I just paste the two classes in here?
[quote]Thats the weird part, I dont touch it anywhere else
[/quote]
Then likely this routine is being run from two different threads simultaneously.
Remember, Threads != routines.
I thaught that was what I was trying to stop with synchronization? And my debug shows me that loopWriteQeue and addToWriteQeue are being run at the same time thats why the exception happens.
You still get the exception after following cfmdobbie’s advise? How do your functions look like now?
Yeah, I created a private Map m; and use that in the synchronization. Here is how it looks like in the methods:
private void loopWriteQeue() {
m = Collections.synchronizedMap(writeQeue);
Set s = m.keySet();
synchronized(m) {
Iterator i = s.iterator();
while(i.hasNext()) {
SpeedChannel key = ((SpeedChannel)i.next());
try { ((SpeedChannel)(writeQeue.get(key))).write(); }
catch(Exception e) { }
if(!((SpeedChannel)writeQeue.get(key)).wantsToWrite()) writeQeue.remove(key);
}
}
}
protected void addToWriteQeue(SpeedChannel add) {
m = Collections.synchronizedMap(writeQeue);
synchronized(m) {
writeQeue.put(add, add);
}
}
You must synchronize on the same object!
The line “m = Collections.synchronizedMap(writeQeue);” will create a new Map object everytime one of your functions are called. It must instead be called only once, preferably when you create writeQueue. Also, after the object m is called, you never, ever use “writeQueue”!!! Always use “m” instead.
You only need to synchronize on m, by useing synchronized(m) {…}, when you are using an iterator. In other words: when you are accessing the map by not using the maps member functions.
Here is how the changes will affect your code. Note that you will have to add a line to you constructor and remove the referance to writeQueue. This is done to make sure you do not use writeQueue.
// constructor
public Queue() {
// do not keep a referance to the unsynchronized map!!
m = Collections.synchronizedMap(new HashMap());
}
private void loopWriteQeue() {
Set s = m.keySet();
synchronized(m) {
Iterator i = s.iterator();
while(i.hasNext()) {
SpeedChannel key = ((SpeedChannel)i.next());
try { ((SpeedChannel)(writeQeue.get(key))).write(); }
catch(Exception e) { }
if(!((SpeedChannel)writeQeue.get(key)).wantsToWrite()) writeQeue.remove(key);
}
}
}
protected void addToWriteQeue(SpeedChannel add) {
// m will synchronize the put function by itself.
writeQeue.put(add, add);
}
Also go over your code againg and make sure that you don’t acces the “m” from any other place. Make it private and don’t return it from any functions in any way.
So you mean that if I make m = Collections.synchronizedMap(writeQeue); m will always have the same content as writeQeue? PErhaps that is what I have missed.
Here is the code now:
private void loopWriteQeue() {
Set s = m.keySet();
synchronized(m) {
Iterator i = s.iterator();
while(i.hasNext()) {
SpeedChannel key = ((SpeedChannel)i.next());
try { ((SpeedChannel)(writeQeue.get(key))).write(); }
catch(Exception e) { }
if(!((SpeedChannel)writeQeue.get(key)).wantsToWrite()) writeQeue.remove(key);
}
}
}
protected void addToWriteQeue(SpeedChannel add) {
writeQeue.put(add, add);
}
And I still get the exception at the same place