Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Synchronized, wait, and notifyall.

Reply
Thread Tools

Synchronized, wait, and notifyall.

 
 
Danny
Guest
Posts: n/a
 
      11-18-2008
Java Gurus:

The method below is called in a meeting application. It lives in a
Tomcat server thread, where it uses a
blocked thread to wait for up to 30 seconds to return an event. When
another thread posts an event for an attendee, it executes a notifyall
on the attendee.

Are there any holes? For example, what can I do to avoid changes to
the attendee, which was being waited upon, between the time the wait
was broken by the notifyall and the attendee is synchronized to
extract and reset its event?

Thanks very much for any advice,

Danny
================================================== =======

public ArrayList<MeetingEvent> getMeetingEvents(String[]
session,
Meeting meeting){
ArrayList<MeetingEvent> events = null;
Attendee attendee = null;
synchronized(attendees){
attendee = attendees.get(session
[ServerUtil.MEMBERSHIP_ID] + "." +
meeting.meetingID + "." + session[ServerUtil.USER_ID]);
}
if( attendee != null ){
if( attendee.events.size() == 0 ){ //If
attendee has no events,
wait for 30 seconds.
try{
synchronized(attendee){
attendee.wait(30000);
}
}
catch (InterruptedException ignored){}
}
synchronized(attendee)
{ //If attendee has events save for the
return and empty for next event.
events = attendee.events;
attendee.events = new
ArrayList<MeetingEvent>();
}
}
return events;
} //End getMeetingEvents

Where MeetingEvent is:

public class MeetingEvent implements IsSerializable{
public int action;
public String[][] value;
public String membershipID;
public String meetingID;
public MeetingEvent(String membershipID, String meetingID, int
action, String[][] initialValue){
this.membershipID = membershipID;
this.meetingID = meetingID;
this.action = action;
value = initialValue;
}
public MeetingEvent(){}
}
 
Reply With Quote
 
 
 
 
Lew
Guest
Posts: n/a
 
      11-18-2008
On Nov 18, 12:27*pm, Danny <dhho...@gmail.com> wrote:
> The method below is called in a meeting application. *It lives in a
> Tomcat server thread, where it uses a


It can be dangerous to explicitly spawn threads from a web app. You
do have several threading issues here, but I won't pretend to give a
complete analysis.

Part of the problem is that your example is incomplete. Read Andrew
Thompson's excellent essay on SSCCEs, mirrored at
<http://sscce.org/>

What version of Java are you using?

> blocked thread to wait for up to 30 seconds to return an event. *When


The first is that your 'wait()' lock occurs inside another lock, which
could quite adversely affect performance.

> another thread posts an event for an attendee, it executes a notifyall
> on the attendee.


If the other thread needs to acquire the lock on 'attendees', it will
be hungry trying to do so, until the waiting thread releases its lock.

> Are there any holes? *For example, what can I do to avoid changes to
> the attendee, which was being waited upon, *between the time the wait
> was broken by the notifyall and the attendee is synchronized to
> extract and reset its event?


The 'attendee' is not fully guarded.

> ================================================== =======
>
> * * * *public ArrayList<MeetingEvent> getMeetingEvents(String[]
> session,
> Meeting meeting){


You should return a 'List' instead of an 'ArrayList', unless there is
some particular reason to lock the implementation down like that.
Same with the declaration of 'events'.

> * * * * * * * *ArrayList<MeetingEvent> events = null;
> * * * * * * * *Attendee attendee = null;
> * * * * * * * *synchronized(attendees){
> * * * * * * * * * * * *attendee = attendees.get(session
> [ServerUtil.MEMBERSHIP_ID] + "." +
> meeting.meetingID + "." + session[ServerUtil.USER_ID]);
> * * * * * * * *}


You might be better off releasing the lock on 'attendees' now, instead
of holding through all the lengthy stuff that might follow.

> * * * * * * * *if( attendee != null ){
> * * * * * * * * * * * *if( attendee.events.size() == 0 ){ * //If
> attendee has no events,
> wait for 30 seconds.


You are not synchronized on 'attendee' here, so it is possible for
this thread to see 'events' equal to 'null', or of size 0, even after
another thread has changed it.

> * * * * * * * * * * * * * * * *try{
> * * * * * * * * * * * * * * * * * * * *synchronized(attendee){
> * * * * * * * * * * * * * * * * * * * * * * * *attendee.wait(30000);


Your indentation is far too aggressive for Usenet. You should limit
indent levels to four spaces or less each.

> * * * * * * * * * * * * * * * * * * * *}
> * * * * * * * * * * * * * * * *}
> * * * * * * * * * * * * * * * *catch (InterruptedException ignored){}
> * * * * * * * * * * * *}
> * * * * * * * * * * * *synchronized(attendee)


This confuses me.

> { * * * * * * * * * * * * //If attendee has events save for the
> return and empty for next event.
> * * * * * * * * * * * * * * * *events = attendee.events;
> * * * * * * * * * * * * * * * *attendee.events = new
> ArrayList<MeetingEvent>();


Because the check for 'attendee.events.size() == 0' is not
synchronized, this change might not be visible to other threads.

> * * * * * * * * * * * *}
> * * * * * * * *}
> * * * * * * * *return events;
> * * * *} * * * //End getMeetingEvents


That's all I have patience for.

--
Lew
 
Reply With Quote
 
 
 
 
Tom Anderson
Guest
Posts: n/a
 
      11-18-2008
On Tue, 18 Nov 2008, Danny wrote:

> The method below is called in a meeting application. It lives in a
> Tomcat server thread, where it uses a blocked thread to wait for up to
> 30 seconds to return an event. When another thread posts an event for
> an attendee, it executes a notifyall on the attendee.


Top suggestion: don't write this. Use a BlockingQueue from
java.util.concurrent instead. No holes, guaranteed (void where prohibited
by law). Attach a queue to each Attendee, have event-deliverers push
events onto the queue with add(), and the event-handler remove them with
the timeout version of poll().

You need to think about what happens when the queue is full - should
event-posters block until it isn't, drop the event on the floor, or deal
with the failure? There are three pushing methods, to reflect those
options.

I'll respond to your questions, but in the spirit of contemplation, rather
than of engineering.

> Are there any holes?


Yes, you don't show us what the event-posting code looks like!

> For example, what can I do to avoid changes to the attendee, which was
> being waited upon, between the time the wait was broken by the notifyall
> and the attendee is synchronized to extract and reset its event?


Why is that a problem? If "changes" means more events being posted, why
can't they just be added to the list?

If you really want it to not happen, you need to give the Attendee two
states: ready to be changed, and has been changed and ready to be read.
The simplest way to do this i can see is to use the state of the events
field - if it's null, it means events can be added, and if not, they
can't:

public class Attendee {
private static final long TIMEOUT = 30000 ;
private List<MeetingEvent> events ; // i don't *think* this needs to be transient
public synchronized List<MeetingEvent> getEvents() {
long deadline = System.currentTimeMillis() + TIMEOUT ;
try {
while (events == null) wait(deadline - System.currentTimeMillis()) ;
}
catch (InterruptedException e) {} // just fall through and probably return the empty list
if (events == null) return Collections.emptyList() ;
List eventsToReturn = events ;
events = null ;
notify() ;
return eventsToReturn ;
}
public synchronized void putEvents(List<MeetingEvent> events) throws InterruptedException {
long deadline = System.currentTimeMillis() + TIMEOUT ;
while (events != null) wait(deadline - System.currentTimeMillis()) ;
if (events != null) throw new InterruptedException("timed out waiting to post events") ;
this.events = events ;
notify() ;
}
}

This is pretty much the classic one-element channel, with some bells and
whistles.

> Thanks very much for any advice,


Apart from the above, your code has pretty iffy OO design. Most of this
should be inside methods on Attendee, not in some external method.

And why doesn't Meeting have a map from membership and/or user ID to
Attendee? Then you wouldn't need a global map for all meetings.

> public ArrayList<MeetingEvent> getMeetingEvents(String[]
> session,
> Meeting meeting){
> ArrayList<MeetingEvent> events = null;
> Attendee attendee = null;
> synchronized(attendees){
> attendee = attendees.get(session
> [ServerUtil.MEMBERSHIP_ID] + "." +
> meeting.meetingID + "." + session[ServerUtil.USER_ID]);
> }
> if( attendee != null ){
> if( attendee.events.size() == 0 ){ //If
> attendee has no events,
> wait for 30 seconds.
> try{
> synchronized(attendee){
> attendee.wait(30000);
> }
> }
> catch (InterruptedException ignored){}
> }
> synchronized(attendee)
> { //If attendee has events save for the
> return and empty for next event.
> events = attendee.events;
> attendee.events = new
> ArrayList<MeetingEvent>();
> }
> }
> return events;
> } //End getMeetingEvents
>
> Where MeetingEvent is:


.... probably the one class here whose implementation really doesn't
matter.

tom

--
If goods don't cross borders, troops will. -- Frédéric Bastiat
 
Reply With Quote
 
Mark Space
Guest
Posts: n/a
 
      11-18-2008
Danny wrote:

> try{
> synchronized(attendee){
> attendee.wait(30000);
> }
> }
> catch (InterruptedException ignored){}


Ignoring interrupts like this is probably a bad idea. I'd restore the
interrupt in the catch block if you don't process it yourself.

In addition, stalling a Tomcat thread for 30 seconds seems like a bad
idea. I'd just refresh every 10-15 seconds in the browser (HTML or
JavaScript) and just return what "events" you have when you get the
request. I've used JavaScript chat apps that run on 10 second timers,
they feel very responsive.


 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      11-18-2008
On Nov 18, 1:18*pm, Eric Sosman <Eric.Sos...@sun.com> wrote:
> Lew wrote:
> > On Nov 18, 12:27 pm, Danny <dhho...@gmail.com> wrote:
> >> [...]
> >> * * * * * * * *Attendee attendee = null;
> >> * * * * * * * *synchronized(attendees){
> >> * * * * * * * * * * * *attendee = attendees.get(session
> >> [ServerUtil.MEMBERSHIP_ID] + "." +
> >> meeting.meetingID + "." + session[ServerUtil.USER_ID]);
> >> * * * * * * * *}

>
> > You might be better off releasing the lock on 'attendees' now, instead
> > of holding through all the lengthy stuff that might follow.

>
> * * *Um, er, it looks like the bletcherous line-wrapping has
> provoked a read error in your eye/brain interface ...


Oh. Oops. Teehee.

--
Lew

 
Reply With Quote
 
Tom Anderson
Guest
Posts: n/a
 
      11-19-2008
On Tue, 18 Nov 2008, Mark Space wrote:

> Danny wrote:
>
>> try{
>> synchronized(attendee){
>> attendee.wait(30000);
>> }
>> }
>> catch (InterruptedException ignored){}

>
> Ignoring interrupts like this is probably a bad idea. I'd restore the
> interrupt in the catch block if you don't process it yourself.


It depends on what you think about interrupts.

What are interrupts for? What is the tao of interrupts?

I tend to think they're a way of saying to a thread "hey, i have something
to say to you, so if you're currently sat in a wait, a sleep, or a
blocking IO operation somewhere, stop that, and get back to running". That
means it isn't important to propagate an InterruptedException, but it is
important to stop doing whatever blocking thing was interrupted. Thus, i
think the OP's code is okay.

> In addition, stalling a Tomcat thread for 30 seconds seems like a bad
> idea. I'd just refresh every 10-15 seconds in the browser (HTML or
> JavaScript) and just return what "events" you have when you get the
> request.


Same here.

tom

--
I only listen to mashups of The Carpenters and ear-bleeding German gabber
-- boomaga
 
Reply With Quote
 
Mark Space
Guest
Posts: n/a
 
      11-19-2008
Tom Anderson wrote:

>>
>> Ignoring interrupts like this is probably a bad idea. I'd restore the
>> interrupt in the catch block if you don't process it yourself.

>
> It depends on what you think about interrupts.
>
> What are interrupts for? What is the tao of interrupts?


In this case, I think it's more of a matter of "what does the container
think of interrupts?"

In other words, it's not my thread, and I don't know what Tomcat wants
to do on an interrupt. Since it's not my thread, I restore the
interrupt so that Tomcat can deal with it however it wants.

By swallowing the interrupt, you are preventing Tomcat from responding
to any internal mechanism that it might have implemented for handling
it's own threads.
 
Reply With Quote
 
Arne Vajhøj
Guest
Posts: n/a
 
      11-19-2008
Tom Anderson wrote:
> On Tue, 18 Nov 2008, Danny wrote:
>> The method below is called in a meeting application. It lives in a
>> Tomcat server thread, where it uses a blocked thread to wait for up to
>> 30 seconds to return an event. When another thread posts an event for
>> an attendee, it executes a notifyall on the attendee.

>
> Top suggestion: don't write this. Use a BlockingQueue from
> java.util.concurrent instead. No holes, guaranteed (void where
> prohibited by law). Attach a queue to each Attendee, have
> event-deliverers push events onto the queue with add(), and the
> event-handler remove them with the timeout version of poll().


Very good advice. With java.util.concurrent then user code
should very rarely need to use synchronized/wait/notifyAll.

Arne
 
Reply With Quote
 
Arne Vajhøj
Guest
Posts: n/a
 
      11-19-2008
Mark Space wrote:
> Tom Anderson wrote:
>>> Ignoring interrupts like this is probably a bad idea. I'd restore
>>> the interrupt in the catch block if you don't process it yourself.

>>
>> It depends on what you think about interrupts.
>>
>> What are interrupts for? What is the tao of interrupts?

>
> In this case, I think it's more of a matter of "what does the container
> think of interrupts?"
>
> In other words, it's not my thread, and I don't know what Tomcat wants
> to do on an interrupt. Since it's not my thread, I restore the
> interrupt so that Tomcat can deal with it however it wants.
>
> By swallowing the interrupt, you are preventing Tomcat from responding
> to any internal mechanism that it might have implemented for handling
> it's own threads.


I am pretty sure that Tomcat would be extremely "surprised"
if it received a checked exception like InterruptedException
from HttpServlet service that does not have such a throws.

Arne
 
Reply With Quote
 
Arne Vajhøj
Guest
Posts: n/a
 
      11-19-2008
Tom Anderson wrote:
> On Tue, 18 Nov 2008, Mark Space wrote:
>> Danny wrote:
>>> try{
>>> synchronized(attendee){
>>> attendee.wait(30000);
>>> }
>>> }
>>> catch (InterruptedException ignored){}

>> In addition, stalling a Tomcat thread for 30 seconds seems like a bad
>> idea. I'd just refresh every 10-15 seconds in the browser (HTML or
>> JavaScript) and just return what "events" you have when you get the
>> request.

>
> Same here.


Me too.

I could be done with some refreshing JavaScript.

But performance and scalability would be much better if there
were some technology used client side that allowed for
push.

Arne
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
if and and vs if and,and titi VHDL 4 03-11-2007 05:23 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57