Have you ever heard about a GUI freezing because of repeated calls to the method javax.swing.Document.insertString?
There is my code:
private int insertNotation(GameNode startNode, StyledDocument doc, int pos) {
String s = "";
int startPos = pos;
boolean isContinuous = false;
Style boldStyle, regularStyle, commentStyle, currentNodeStyle, nagStyle, grayStyle;
grayStyle = notationTextPane.getStyle("gray");
GameNode currentNode = history.getCurrentGameNode();
if ((currentNode.isLeaf() && startNode == currentNode.getParent()) || startNode == currentNode) {
try {
if (startNode.getComment().length() > 0) {
s = startNode.getComment() + " ";
commentStyle.addAttribute("gameNode", startNode);
doc.insertString(pos, s, commentStyle);
pos += s.length();
}
for (int n = 0; n < startNode.getChildCount(); n++) {
GameNode node = (GameNode) startNode.getChildAt(n);
boolean isCurrentNode = (node == currentNode);
if (node.isLeaf()) {
if (node.isWhiteMove()) {
s = node.getFullMoveNumber() + ". ";
boldStyle.addAttribute("gameNode", node);
doc.insertString(pos, s, boldStyle);
pos += s.length();
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
isContinuous = true;
} else {
if (isContinuous) {
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
} else {
isContinuous = true;
s = node.getFullMoveNumber() + "... ";
boldStyle.addAttribute("gameNode", node);
doc.insertString(pos, s, boldStyle);
pos += s.length();
s = node.getMove();
Style style = isCurrentNode ? currentNodeStyle : regularStyle;
style.addAttribute("gameNode", node);
doc.insertString(pos, s, style);
pos += s.length();
}
}
doc.insertString(pos++, " ", regularStyle);
}
} catch (BadLocationException e) {
e.printStackTrace();
}
return pos - startPos;
}
I simplified it a lot but as you can see, there are many calls to the insertString() method in my 'doc' StyledDocument variable.
This StyledDocument object is added in a JTabbedPane.
I have read here (in Performance Analysis section) that javax.swing.Document.insertString method is extremely slow (here over 1 ms per call).
Can repeated calls to it freeze the GUI?
Whenever you do something slow in the main GUI thread, you will freeze the GUI. It redraws based on processing events. Imagine that your event handling code is in a while loop pulling events off a queue -- if, you don't return from your function, the next event can't be processed.
Consider doing long-running or slow processing in a background thread.
See this article: http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
Consider using a background thread to throttle the addition of text to your document. This is best accomplished using a SwingWorker.
First we define queue for throttling. Requests to insert text will simply add to this queue. These requests do not have to be on the Event Dispatch thread.
BlockingQueue<String> toAdd = new LinkedBlockingQueue<String>();
toAdd.add("Some text");
toAdd.add("Some more text");
Next we invoke SwingWorker where background thread continuously polls the queue and publishes results back to the Event Dispatch thread in chunks.
new SwingWorker<Void, String>() {
// Implementation of 'process' and 'doInBackground' methods to go here.
}.execute();
Now we implement doInBackground to poll until input queue is empty and then publish back to the Event Dispatch thread in one go for more efficient throttling.
public String doInBackground() {
while (!Thread.interrupted()) {
List<String> l = new LinkedList<String>();
String s = toAdd.poll();
if (s == null) {
publish(l.toArray(new String[l.size()]));
l.clear();
} else {
l.add(s);
}
}
// Thread interrupted but publish anything pending before returning.
if (!l.isEmpty()) {
publish(l.toArray(new String[l.size()]));
}
return null;
}
Finally we implement process. This is called on Swing thread following a call to publish on the background thread. We join chunks together using a StringBuilder to avoid the need for multiple inserts into document (this the main advantage with this approach).
public void process(String... chunks) {
StringBuilder sb = new StringBuilder();
for (String chunk : chunks) {
sb.append(chunk);
}
// Insert sb.toString() into buffer HERE
}
Are you actually seeing a freeze (no forward progress), or just very severe slowdown?
We don't have your entire program code, so it's not clear, but is your document already being displayed when you start changing it? Each call to insertString not only modifies the document, but creates a cascade of events, to which the GUI reacts.
If you are essentially building the document and you only want to display the final state, you may want to consider building a new document before adding it to the text component widget, and then setting the ready document. It'll be a little more flicker, but much faster, since until you add your document to the widget with setDocument, you're not really triggering events and updates.
You can then build the document on a different thread.
The problem is gone as I used a SwingWorker object to alleviate the GUI thread: the calls to javax.swing.Document.insertString was in this SwingWorker.
I also had an other resolved bug: there was too many graphics overloaded treatment process.
In this case too I used a SwingWorker.
Here is the code:
new SwingWorker<Void, Void>()
{
#Override
protected Void doInBackground() throws Exception
{
visualBoard.update(); // here your method call, deported from the GUI thread.
return null;
}
}.execute();
And thank you all for your so fast answers!!
This can actually happen. It's not about doing too much either; the method just hangs (CPU=0%).
Here's an example to test: http://tinybrain.de/1000816
When the bug occurs, you will not see a frame at all, and the last line on the console is "inserting".
It's kind of a severe bug that speaks against using JTextPane at all...
Edit: I have now reproduced the freezing with JTextArea.append too! http://tinybrain.de/1000822
It seems to revolve around always creating the elements in an AWT thread. Previously, I created them just in main(), and calling SwingUtilities.invokeLater there with the appends. THAT seems to be wrong.
If I create the whole GUI within an "awt {}" block (JavaX shorthand for SwingUtilities.invokeLater), everything is fine, as you can see here: http://tinybrain.de/1000823.
Cheers
You should look into running your repeated call within a Thread. That should do it for you.
Related
This question already has answers here:
How to shutdown an ExecutorService?
(3 answers)
Closed 6 years ago.
I am trying to build a program that attempts to decrypt a file that has been encrypted using AES encryption for a school project. I have a list of ~100,000 english words, and want to implement multithreading within the program to optimise the time taken to attempt decryption with every word in the file.
I am having a problem when trying to stop the rest of the dictionary being searched in the event of the decryption completing successfully - "Attempting shutdown" is being printed to the console, but it appears that the threads continue working through the rest of the dictionary before the executor stops allocating new threads.
In my main program, the threads are run using this method:
private void startThreads(){
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
System.out.println("Maximum threads inside pool " + executor.getMaximumPoolSize());
for (int i = 0; i < dict.size(); i++) {
String word = dict.get(i);
Grafter grafter = new Grafter("Grafter " + i,word);
grafter.registerWorkerListener(thread -> {
List results = thread.getResults();
for (Iterator iter = results.iterator(); iter.hasNext();) {
found = (boolean) iter.next();
if(found){
System.out.println("THE WORD HAS BEEN FOUND!! Attempting shutdown");
executor.shutdown();
}
}
});
// Start the worker thread
Thread thread = new Thread(grafter);
thread.start();
}
if(!executor.isShutdown()) {
executor.shutdown();
}
}
And the implementation of the 'grafter' runnable class is as follows:
public class Grafter implements Runnable{
private String NAME;
private final String WORD;
private List listeners = new ArrayList();
private List results;
public Grafter(String name, String word){
NAME = name;
WORD = word;
}
public String getName(){
return NAME;
}
#Override
public void run() {
if (tryToDecrypt(WORD) == true){
System.out.println("Thread: '" + NAME + "' successfully decrypted using word: \"" + WORD + "\".");
results = new ArrayList();
results.add(true);
// Work done, notify listeners
notifyListeners();
}else{
results = new ArrayList();
results.add(false);
// Work done, notify listeners
notifyListeners();
}
}
private void notifyListeners() {
for (Iterator iter = listeners.iterator(); iter.hasNext();) {
GrafterListener listener = (GrafterListener) iter.next();
listener.workDone(this);
}
}
public void registerWorkerListener(GrafterListener listener) {
listeners.add(listener);
}
public List getResults() {
return results;
}
private boolean tryToDecrypt(String word){
//Decryption performed, returning true if successfully decrypted,
//Returns false if not
}
}
The correct word is right at the top of the dictionary, so success is found early in the program's execution. However, there is a long pause (as the rest of the dictionary is worked through) before the program finishes.
I am looking for help on the positioning of executor.shutdown(), and how to stop the remainder of the dictionary being parsed after the decryption successfully completes.
Your main problem is that you're not actually submitting your runnables to the executor. Thus calling shutdown on the executor has no effect on all the threads you've spawned.
Instead of creating a new thread instead do something like:
executor.submit(grafter)
This should get you most of the way, but if you want the service to shut down promptly and cleanly there's a bit more you can do. The link provided in the comments by shmosel should help you with that.
As an aside, the way you're doing this isn't going to be very efficient I don't think. Essentially you're creating a new task for every word in your dictionary which means that you have a large number of tasks (100K in your case). This means that the overhead of managing and scheduling all those task is likely to be a significant portion of the work performed by your program. Instead you may want to break the list of words up into some number of sublists each containing an equal number of words and then make your runnable process that sublist only.
One of my applications paints objects to a screen by reading an array List:
simple code summary:
#Override
public synchronized void paintComponent(Graphics g) {
for(Object gO:paintList) {
g.drawImage( gO.getObjImage(), gO.getXLoc(), gO.getYLoc(), outer.getTransparent(), null);
}
}
The problem is I add more objects every time the user clicks the mouse, so if if the user clicks fast enough, I can cause the program painting to stutter since it cannot read while it is writing (the arrayList is synchronized). What is the common practice developers use to deal with this concurrency issue?
edit: here is the code that calls the repaint:
byte ticks = 0;
while(true) {
currentTime = System.nanoTime();
if(ticks == 25) {
drawPanel.repaint();
ticks = 0;
} else if (ticks%5 == 0) {//if ticks is a multiple of 5 (5,10,15,...)
drawPanel.operations();
ticks++;
} else if(ticks < 25) {
ticks++;
}
try {
/*
Refer to: 'http://stackoverflow.com/questions/1036754/difference-between-wait-and-sleep'
on differences between Thread.sleep() and wait()
*/
wait(1);//old timings: (long)(refreshRate*1000)
} catch (InterruptedException ex) {
Logger.getLogger(DeathWish.class.getName()).log(Level.SEVERE, null, ex);
}
//Debugging
//System.out.println(ticks);
currentTime = System.nanoTime();
*where operations() calculates changes in 'paintable' object's properties, removes objects that meet certain conditions and adds new objects to the paint list. Logically to me, the adding and writing should be separated?
I can post the operations() method, if there isn't enough information, but I'm trying not to post huge sections of code so it is easier to interpret.
I guess you're getting things a bit wrong with synchronization :
ArrayList is a list implemented without synchronization
A synchronized method means that only 1 Thread at a time can access the method, but the variables inside your function are not synchronized at all
What you want, is to have your list temporary synchronized.
You could do something like that :
#Override
public void paintComponent(Graphics g) {
synchronized(paintList) {
for(Object gO:paintList) {
g.drawImage( gO.getObjImage(), gO.getXLoc(), gO.getYLoc(), outer.getTransparent(), null);
}
}
}
and in the code adding objects yo your list do somewhat the same.
EDIT from here :
If you want to remove all the concurrency problems between the add thread and the paint Thread, here's how you could do :
in the method to add images :
public synchronized void addImage(...) {
Something newImage = .....
List<Something> newPaintList = new ArrayList<>(paintList.size() + 1);
newPaintList.addAll(paintList);
newPaintList.add(newImage);
paintList = newPaintList;
}
And in the paint method, remove the synchronization part.
#Override
public void paintComponent(Graphics g) {
for(Object gO:paintList) {
g.drawImage( gO.getObjImage(), gO.getXLoc(), gO.getYLoc(), outer.getTransparent(), null);
}
}
With this, you won't have any concurrency between the reads and the writes, since the only operation done on paintList is reads.
The addImage should be synchronized to avoid two different Threads adding images at the same time, which could make one addImage ignored.
While the following code is executing, I want the ability to stop it from running, even if it's halfway done. For example, if I have a boolean x = true; then I want to send some king of stop(ReadJSONFeedMainTask); command to this thread/class. I want to cancel everything, even what may be occurring in onPostExecute(). How can I do this?
Here is my code:
private void doStuff() {
new ReadJSONFeedMainTask().execute(urlString);
}
private class ReadJSONFeedMainTask extends AsyncTask<String, Void, String> {
protected String doInBackground(String... urls) {
return Helper.readJSONFeed(urls[0]);
}
protected void onPostExecute(String result) {
try {
dictItems = new ArrayList<HashMap<?, ?>>();
JSONArray jsonArray = new JSONArray(result);
Log.i("JSON", "Number of items in feed: " + jsonArray.length());
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
HashMap<String, String> map = new HashMap<String, String>();
Iterator<?> it = jsonObject.keys();
while (it.hasNext())
{
// do a bunch of time consuming stuff
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
To cancel the task, do asyncTaskVariable.cancel(). If it's already in onPostExecute it's going to have to finish it though- you're on the UI thread and can't kill it. Best you can do there is set a variable that tells it to terminate (from another thread of course), and have it check that variable periodically, returning if its set.
Just skip out when x is true
while (it.hasNext() && x == false) // where x is your skip boolean
{
// do a bunch of time consuming stuff
}
and do similar in any other methods you want to terminate.
Its much safer than terminating it mid flow, as this can cause unusual behaviour.
You should call
asyncTaskVariable.cancel( true );
this will force the asynctask to stop.
BUT, you should know that is won't stop immediatly.
If the thread is waiting for IO input, it will be in the "blocked io state". Then it won't be stopped. If you don't have control over what happens in this Helper method, you can't change anything. But if you own it, then you can loop through the input stream and read it by a number of bytes each time (using a buffer). This will give you opportunities to stop your threead. For that do something like
int readBytes = 0;
while( readBytes != -1 && !Thread.interrupted ) {
readBytes = bufferedReader.read( buffer );
if( readBytes >0 ) {
stringBuilder.append( buffer, 0, readBytes );
}
}
using the interrupted flag, you will be able to stop your thread when your asynctask is cancelled.
You should consider not using an AsyncTask for your network operations. They have been designed for short living tasks.
AsyncTask is designed to be a helper class around Thread and Handler
and does not constitute a generic threading framework. AsyncTasks
should ideally be used for short operations (a few seconds at the
most.) If you need to keep threads running for long periods of time,
it is highly recommended you use the various APIs provided by the
java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and
FutureTask.
An alternative is to use an Android Service, like with the RoboSpice library.
You need to keep a reference to your class in order to do that. For example...
private ReadJSONFeedMainTask foo;
private void doStuff() {
foo = new ReadJSONFeedMainTask();
foo.execute(urlString);
}
Then later on when you have an interrupt event you can just call
foo.cancel(true);
The true parameter indicates an immediate interrupt.
EDIT I didnt notice your placement of the longrunning task in the postexecute. As gabe mentioned correctly, even cancel() will not help you there. You will need to refactor your code to put the longrunning task in the doInBackground method so that it is immediately cancelable, in addition to holding a reference to it as above.
I'm writing a game engine which performs alhpa-beta search on at a game state, and I'm trying to parallelize it. What I have so far is working at first, and then it seems to slow to a halt. I suspect that this is because I'm not correctly disposing of my threads.
When playing against the computer, the game calls on the getMove() function of a MultiThreadedComputerPlayer object. Here is the code for that method:
public void getMove(){
int n = board.legalMoves.size();
threadList = new ArrayList<WeightedMultiThread>();
moveEvals = new HashMap<Tuple, Integer>();
// Whenever a thread finishes its work at a given depth, it awaits() the other threads
// When all threads are finished, the move evaluations are updated and the threads continue their work.
CyclicBarrier barrier = new CyclicBarrier(n, new Runnable(){
public void run() {
for(WeightedMultiThread t : threadList){
moveEvals.put(t.move, t.eval);
}
}
});
// Prepare and start the threads
for (Tuple move : board.legalMoves) {
MCBoard nextBoard = board.clone();
nextBoard.move(move);
threadList.add(new WeightedMultiThread(nextBoard, weights, barrier));
moveEvals.put(move, 0);
}
for (WeightedMultiThread t : threadList) {t.start();}
// Let the threads run for the maximum amount of time per move
try {
Thread.sleep(timePerMove);
} catch (InterruptedException e) {System.out.println(e);}
for (WeightedMultiThread t : threadList) {
t.stop();
}
// Play the best move
Integer best = infHolder.MIN;
Tuple nextMove = board.legalMoves.get(0);
for (Tuple m : board.legalMoves) {
if (moveEvals.get(m) > best) {
best = moveEvals.get(m);
nextMove = m;
}
}
System.out.println(nextMove + " is the choice of " + name + " given evals:");
for (WeightedMultiThread t : threadList) {
System.out.println(t);
}
board.move(nextMove);
}
And here run() method of the threads in question:
public void run() {
startTime = System.currentTimeMillis();
while(true) {
int nextEval = alphabeta(0, infHolder.MIN, infHolder.MAX);
try{barrier.await();} catch (Exception e) {}
eval = nextEval;
depth += 1;
}
}
I need to be able to interrupt all the threads when time is up-- how am I supposed to implement this? As of now I'm constantly catching (and ignoring) InterruptedExceptions.
Thread.stop was deprecated for a reason. When you interrupt a thread in the middle, the thread doesn't have the chance to properly release resources it was using, and doesn't notify other threads of its completion...something that's very important in multi-threaded apps. I'm not surprised your performance tanks; I would be willing to bet your memory usage shoots through the roof. You also don't recycle the threads, you start and stop them without creating new objects, which means whatever broken state the variables were left in is probably still plaguing them.
A better way is to set a flag that tells the thread it should return. So include in your WeightedMultiThread class a boolean named something like shouldQuit, and set it to false every time start() is called. Then, instead of while (true) do while (!shouldQuit), and instead of t.stop(), use t.shouldQuit = true. After you do that to every thread, have another loop that checks each thread for t.isAlive(), and once every thread has returned, go about your business. You should have much better results that way.
This looks like an ideal place to use an ExecutorService. You can create Callable instances that implement the parallel tasks, submit them to the ExecutorService, then use awaitTermination to enforce a timeout.
For example:
public void getMove() {
ExecutorService service = Executors.newFixedThreadPool(board.legalMoves.size());
List<Future<Something>> futures = new ArrayList<Future<Something>>(board.legalMoves.size());
for (Tuple move : board.legalMoves) {
futures.add(service.submit(new WeightedMultiThread(...)));
}
service.awaitTermination(timePerMove, TimeUnit.MILLISECONDS);
service.shutdownNow(); // Terminate all still-running jobs
for (Future<Something> future : futures) {
if (future.isDone()) {
Something something = future.get();
// Add best move logic here
}
}
...
}
Replace Something with something that encapsulates information about the move that has been evaluated. I'd suggest Something be a class that holds the Tuple and its associated score. Your WeightedMultiThread class can do something like this:
class WeightedMultiThread implements Callable<Something> {
public Something call() {
// Compute score
...
// Return an appropriate data structure
return new Something(tuple, score);
}
}
Even better would be to create the ExecutorService once and re-use it for each call to getMove. Creating threads is expensive, so best to only do it once if you can. If you take this approach then you should not call shutdownNow, but instead use the Future.cancel method to terminate jobs that have not completed in time. Make sure your WeightedMultiThread implementation checks for thread interruption and throws an InterruptedException. That's usually a good way to write a long-running task that needs to be interruptible.
EDIT:
Since you're doing a level-by-level exploration of the game space, I'd suggest that you encode that in the getMove function rather than in the Tuple evaluation code, e.g.
public Tuple getMove() {
ExecutorService service = ...
Tuple best = null;
long timeRemaining = MAX_TIME;
for (int depth = 0; depth < MAX_DEPTH && timeRemaining > 0; ++depth) {
long start = System.currentTimeMillis();
best = evaluateMoves(depth, service, timeRemaining);
long end = System.currentTimeMillis();
timeRemaining -= (end - start);
}
return best;
}
private Tuple evaluateMoves(int depth, ExecutorService service, long timeRemaining) {
List<Future<Whatever>> futures = service.submit(...); // Create all jobs at this depth
service.awaitTermination(timeRemaining, TimeUnit.MILLISECONDS);
// Find best move
...
return best;
}
That could probably be cleaner, but you get the idea.
The most sensitive way is to use interruption mechanism. Thread.interrupt() and Thread.isInterrupted() methods. This ensures your message will be delivered to a thread even if it sits inside a blocking call (remember some methods declare throwing InterruptedException?)
P.S. It would be useful to read Brian Goetz's "Java Concurrency in Practice" Chapter 7: Cancellation and Shutdown.
I am writing a multithreaded parser.
Parser class is as follows.
public class Parser extends HTMLEditorKit.ParserCallback implements Runnable {
private static List<Station> itemList = Collections.synchronizedList(new ArrayList<Item>());
private boolean h2Tag = false;
private int count;
private static int threadCount = 0;
public static List<Item> parse() {
for (int i = 1; i <= 1000; i++) { //1000 of the same type of pages that need to parse
while (threadCount == 20) { //limit the number of simultaneous threads
try {
Thread.sleep(50);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
Thread thread = new Thread(new Parser());
thread.setName(Integer.toString(i));
threadCount++; //increase the number of working threads
thread.start();
}
return itemList;
}
public void run() {
//Here is a piece of code responsible for creating links based on
//the thread name and passed as a parameter remained i,
//connection, start parsing, etc.
//In general, nothing special. Therefore, I won't paste it here.
threadCount--; //reduce the number of running threads when current stops
}
private static void addItem(Item item) {
itenList.add(item);
}
//This method retrieves the necessary information after the H2 tag is detected
#Override
public void handleText(char[] data, int pos) {
if (h2Tag) {
String itemName = new String(data).trim();
//Item - the item on which we receive information from a Web page
Item item = new Item();
item.setName(itemName);
item.setId(count);
addItem(item);
//Display information about an item in the console
System.out.println(count + " = " + itemName);
}
}
#Override
public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
if (HTML.Tag.H2 == t) {
h2Tag = true;
}
}
#Override
public void handleEndTag(HTML.Tag t, int pos) {
if (HTML.Tag.H2 == t) {
h2Tag = false;
}
}
}
From another class parser runs as follows:
List<Item> list = Parser.parse();
All is good, but there is a problem. At the end of parsing in the final list "List itemList" contains 980 elements onto, instead of 1000. But in the console there is all of 1000 elements (items). That is, some threads for some reason did not call in the handleText method the addItem method.
I already tried to change the type of itemList to ArrayList, CopyOnWriteArrayList, Vector. Makes the method addItem synchronized, changed its call on the synchronized block. All this only changes the number of elements a little, but the final thousand can not be obtained.
I also tried to parse a smaller number of pages (ten). As the result the list is empty, but in the console all 10.
If I remove multi-threading, then everything works fine, but, of course, slowly. That's not good.
If decrease the number of concurrent threads, the number of items in the list is close to the desired 1000, if increase - a little distanced from 1000. That is, I think, there is a struggle for the ability to record to the list. But then why are synchronization not working?
What's the problem?
After your parse() call returns, all of your 1000 Threads have been started, but it is not guaranteed that they are finished. In fact, they aren't that's the problem you see. I would heavily recommend not write this by yourself but use the tools provided for this kind of job by the SDK.
The documentation Thread Pools and the ThreadPoolExecutor are e.g. a good starting point. Again, don't implement this yourself if you are not absolutely sure you have too, because writing such multi-threading code is pure pain.
Your code should look something like this:
ExecutorService executor = Executors.newFixedThreadPool(20);
List<Future<?>> futures = new ArrayList<Future<?>>(1000);
for (int i = 0; i < 1000; i++) {
futures.add(executor.submit(new Runnable() {...}));
}
for (Future<?> f : futures) {
f.get();
}
There is no problem with the code, it is working as you have coded. the problem is with the last iteration. rest all iterations will work properly, but during the last iteration which is from 980 to 1000, the threads are created, but the main process, does not waits for the other thread to complete, and then return the list. therefore you will be getting some odd number between 980 to 1000, if you are working with 20 threads at a time.
Now you can try adding Thread.wait(50), before returning the list, in that case your main thread will wait, some time, and may be by the time, other threads might finish the processing.
or you can use some syncronization API from java. Instead of Thread.wait(), use CountDownLatch, this will help you to wait for the threads to complete the processing, and then you can create new threads.