From my experience, the overhead of ActiveMQ and other AMQP implementations (RabbitMQ) are not really useful 99% of the time.
While the latest version of RabbitMQ added a new way to persist messages to the disk instead of saving everything in memory (thus allowing more graceful handling of spikes where you get a LOT more messages queued), I still prefer easier solutions.
It's written in Scala (a language that runs on top of JVM and allows access to all of the Java goodness) and uses the memcaced protocol with minor syntax addons for (sort of) transactional syntax (a little more info on the syntax sugar:
http://robey.lag.net/2008/11/27/scarling-to-kestrel.html).
Any memcached client that uses the text protocol can be used as a client (with all the relevant client based key hashing to work with more servers and distribute load).
The best feature, in my mind, is the flow-to-disk. In cases where you have spikes (like Twitter has in breaking news events and such) and if certain conditions are met (total memory of the queue is larger than X and/or more than Y messages), it will throw the messages directly to the disk and will allow you to still process messages (reading from disk to memory when you empty the memory of the queue).
This allows very easy integration (any memcached client) with a very easy and rather configurable server that can handle load spikes easily.