There are lots of options but it will be hard to find one which fits all your needs.
In your case, try this approach:
- Create a queue.
- Put URLs to download into this queue (or "config objects" which contain the URL and other data like the user name, the destination file, etc).
- Create a pool of threads
- Each thread should try to fetch a URL (or a config object) from the queue and process it.
Use another thread to collect the results (i.e. another queue). When the number of result objects == number of puts in the first queue, then you're finished.
Make sure that all communication goes via the queue or the "config object". Avoid accessing data structures which are shared between threads. This should save you 99% of the problems.