I have made a function that takes x amount of parameters. Each parameter, represents a file.
I want every single of these files to be assigned a thread, for counting the words of the files as fast as possible. Currently I have done something that seems to work, however I find myself in trouble of checking the threads as they are all just assigned with the name "t"
It would be nice to somehow increment the name of the threads. The first thread would be t1 and would be assigned to the first file and so on.
for (File file : fileList) {
final File f = file;
Thread t = null;
ThreadGroup test = null;
Runnable r = new Runnable() {
public void run() {
Scanner fileScan;
try {
fileScan = new Scanner(f);
}
catch(FileNotFoundException e){
System.out.println("Something went wrong while accessing the file");
return;
}
int words = 0;
while (fileScan.hasNext()) {
words++;
fileScan.next();
}
System.out.println(f.getName() + ": " + words + " words");
System.out.println(Thread.activeCount() + ": ");
}
};
t = new Thread(r);
t.start();
}
The threadcount goes up as it is supposed when checking with Thread.activeCount(), but I have no clue how to ever contact them as I have assigned all with the name t, which makes it hard to make yet another thread that shall wait for their output.
I hope my explaination clearified the problem :/
Edit:
The idea is that I will count the amount of words in different files, every file needs to be assigned a thread for itself to speed it up. Other than that, I want one thread waiting for the output from all the other threads ( meaning I will have to wait for them to finish, hence why I would appriciate accessing the name of the threads ).
At the end that last thread that has been waiting will use the collected data for it's own actions before closing the program down.
In order to for instance wait for the threads to finish, you need to save references to the threads you create. You could for instance do
List<Thread> threads = new ArrayList<>();
and then do threads.add(t); at the end of the loop.
After that you could wait for them to finish by doing
for (Thread t : threads) {
t.join();
}
What's problematic however is that there is no way for you to read the result of the threads you've started.
A much better approach to this is to use an ExecutorService and a ThreadPoolExecutor. This way you can submit Callable<Integer> instead of Runnable, and you'll be able to get the result of the word-count.
Here's an outline to get you started:
ExecutorService service = Executors.newFixedThreadPool(numThreads);
List<Future<Integer>> results = new ArrayList<>();
for (File f : fileList) {
results.add(service.submit(new Callable<Integer>() {
// ...
}));
}
for (Future<Integer> result : results) {
System.out.println("Result: " + result.get());
}
Related
I'm having troubles trying to stop my program that has multiple threads running, all threads running are trying to find the same solution but once one thread finds the solution all other threads are to stop.
In the main method I have created a thread group and add threads to it using a for loop and start them
ThreadGroup tg = new ThreadGroup("thread group");
Thread th;
for(int i = 0; i<4; i++){
th = new Thread(tg, new Runnable(), "Thread " + i)
th.start();
}
in the class that implements Runnable I am having troubles trying to figure out how to make it so that once one of the thread finds a solution all the threads will stop. What ends up happening is that either the other threads keep running and sometimes the threads will interupt each other and write over each other.
You have to interrupt those thread (and handle interruption in the runnable). I also not sure if you should use ThreadGroup - I remember seeing a Sonar warning about them.
You would perhaps better have to an ExecutorService and do that using a CountDownLatch (that's one way to do that):
ExecutorService es = Executors.newFixedThreadPool(100);
CountDownLatch cdl = new CountDownLatch(1);
for (int i = 0; i < 100; ++i) {
es.submit(() -> {
Thread.sleep(TimeUnit.SECONDS.toMillis(30)); // + exception handling
cdl.countDown();
});
}
cdl.await(); // or await(5, TimeUnit.MINUTES);
es.shutdownNow();
The trick is:
You create an ExecutorService with a pool of 100 threads.
You create a CoundDownLatch - a barrier - with a count of 1.
You submit your task which, when their job is done, invoke cdl.countDown(); reducing the counter from 1 to 0.
The parent thread wait for the CountDownLatch to reduce to 0 - you should probably use the second version (to block until 5 minutes for example).
If all Runnable fails, you won't have a result: either use a maximum await time, either you could add another CountDownLatch, this time with a count of 100 (the number of threads), countDown() in a try/finally, and in another thread, interrupt the one awaiting on the cdl. You could also do that in a loop:
CountDownLatch allCdl = new CountDownLatch(100);
for (;allCdl.getCount() != 0;) {
if (!cdl.await(60, TimeUnit.SECONDS)) {
if (allCdl.getCount() == 0) {
break;
}
}
}
However, the javadoc of getCount() mention that This method is typically used for debugging and testing purposes. (see CyclicBarrier). Not sure if this is the correct usage.
Upon realizing that a solution has been found, the victorious thread should signal the parent – which then signals all other children to stop, or simply kills them.
ThreadGroup tg = new ThreadGroup("thread group");
CountDownLatch latch = new CountDownLatch(1);
AtomicInteger result = new AtomicInteger();
Random random = new Random();
for (int i = 0; i < 4; i++) {
Thread th = new Thread(tg, () -> {
try {
Thread.sleep(random.nextInt(10000));
result.set(42);
latch.countDown();
System.out.println(Thread.currentThread().getName() + " completed task first");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " was interrupted before it could finish the task");
}
}, "Thread " + i);
th.start();
}
while (latch.getCount() > 0) {
try {
latch.await();
} catch (InterruptedException ignored) {
}
}
tg.interrupt();
System.out.println("The result is " + result.get());
This example shows how to wait until a thread finishes.
You need to make sure your action is interruptible. Thread.sleep as shown in this example is interrubtible by default. See oracle docs for more info.
Also note that it is impossible to guarantee that all other threads will be interrupted before they complete. If you need to make sure to handle only one result, synchronize the access to your result variable and discard any changes beyond the first.
I wanted to try out ForkJoinPool in Java 8 so i wrote a small program for searching all the files whose name contains a specific keyword in a given directory.
Program:
public class DirectoryService {
public static void main(String[] args) {
FileSearchRecursiveTask task = new FileSearchRecursiveTask("./DIR");
ForkJoinPool pool = (ForkJoinPool) Executors.newWorkStealingPool();
List<String> files = pool.invoke(task);
pool.shutdown();
System.out.println("Total no of files with hello" + files.size());
}
}
class FileSearchRecursiveTask extends RecursiveTask<List<String>> {
private String path;
public FileSearchRecursiveTask(String path) {
this.path = path;
}
#Override
protected List<String> compute() {
File mainDirectory = new File(path);
List<String> filetedFileList = new ArrayList<>();
List<FileSearchRecursiveTask> recursiveTasks = new ArrayList<>();
if(mainDirectory.isDirectory()) {
System.out.println(Thread.currentThread() + " - Directory is " + mainDirectory.getName());
if(mainDirectory.canRead()) {
File[] fileList = mainDirectory.listFiles();
for(File file : fileList) {
System.out.println(Thread.currentThread() + "Looking into:" + file.getAbsolutePath());
if(file.isDirectory()) {
FileSearchRecursiveTask task = new FileSearchRecursiveTask(file.getAbsolutePath());
recursiveTasks.add(task);
task.fork();
} else {
if (file.getName().contains("hello")) {
System.out.println(file.getName());
filetedFileList.add(file.getName());
}
}
}
}
for(FileSearchRecursiveTask task : recursiveTasks) {
filetedFileList.addAll(task.join());
}
}
return filetedFileList;
}
}
This program works fine when directory doesn't have too many sub-directories and files but if its really big then it throws OutOfMemoryError.
My understanding is that max number of threads (including compensation threads) are bounded so why their is this error? Am i missing anything in my program?
Caused by: java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:714)
at java.util.concurrent.ForkJoinPool.createWorker(ForkJoinPool.java:1486)
at java.util.concurrent.ForkJoinPool.tryCompensate(ForkJoinPool.java:2020)
at java.util.concurrent.ForkJoinPool.awaitJoin(ForkJoinPool.java:2057)
at java.util.concurrent.ForkJoinTask.doJoin(ForkJoinTask.java:390)
at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:719)
at FileSearchRecursiveTask.compute(DirectoryService.java:51)
at FileSearchRecursiveTask.compute(DirectoryService.java:20)
at java.util.concurrent.RecursiveTask.exec(RecursiveTask.java:94)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1107)
at java.util.concurrent.ForkJoinPool.awaitJoin(ForkJoinPool.java:2046)
at java.util.concurrent.ForkJoinTask.doJoin(ForkJoinTask.java:390)
at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:719)
at FileSearchRecursiveTask.compute(DirectoryService.java:51)
at FileSearchRecursiveTask.compute(DirectoryService.java:20)
at java.util.concurrent.RecursiveTask.exec(RecursiveTask.java:94)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
You should not fork new tasks beyond all recognition. Basically, you should fork as long as there’s a chance that another worker thread can pick up the forked job and evaluate locally otherwise. Then, once you have forked a task, don’t call join() right afterwards. While the underlying framework will start compensation threads to ensure that your jobs will proceed instead of just having all threads blocked waiting for a sub-task, this will create that large amount of threads that may exceed the system’s capabilities.
Here is a revised version of your code:
public class DirectoryService {
public static void main(String[] args) {
FileSearchRecursiveTask task = new FileSearchRecursiveTask(new File("./DIR"));
List<String> files = task.invoke();
System.out.println("Total no of files with hello " + files.size());
}
}
class FileSearchRecursiveTask extends RecursiveTask<List<String>> {
private static final int TARGET_SURPLUS = 3;
private File path;
public FileSearchRecursiveTask(File file) {
this.path = file;
}
#Override
protected List<String> compute() {
File directory = path;
if(directory.isDirectory() && directory.canRead()) {
System.out.println(Thread.currentThread() + " - Directory is " + directory.getName());
return scan(directory);
}
return Collections.emptyList();
}
private List<String> scan(File directory)
{
File[] fileList = directory.listFiles();
if(fileList == null || fileList.length == 0) return Collections.emptyList();
List<FileSearchRecursiveTask> recursiveTasks = new ArrayList<>();
List<String> filteredFileList = new ArrayList<>();
for(File file: fileList) {
System.out.println(Thread.currentThread() + "Looking into:" + file.getAbsolutePath());
if(file.isDirectory())
{
if(getSurplusQueuedTaskCount() < TARGET_SURPLUS)
{
FileSearchRecursiveTask task = new FileSearchRecursiveTask(file);
recursiveTasks.add(task);
task.fork();
}
else filteredFileList.addAll(scan(file));
}
else if(file.getName().contains("hello")) {
filteredFileList.add(file.getAbsolutePath());
}
}
for(int ix = recursiveTasks.size() - 1; ix >= 0; ix--) {
FileSearchRecursiveTask task = recursiveTasks.get(ix);
if(task.tryUnfork()) task.complete(scan(task.path));
}
for(FileSearchRecursiveTask task: recursiveTasks) {
filteredFileList.addAll(task.join());
}
return filteredFileList;
}
}
The method doing the processing has been factored out into a method receiving the directory as parameter, so we are able to use it locally for arbitrary directories not necessarily being associated with a FileSearchRecursiveTask instance.
Then, the method uses getSurplusQueuedTaskCount() to determine the number of locally enqueued tasks which have not been picked up by other worker threads. Ensuring that there are some helps work balancing. But if this number exceeds the threshold, the processing will be done locally without forking more jobs.
After the local processing, it iterates over the tasks and uses tryUnfork() to identify jobs which have not been stolen by other worker threads and process them locally. Iterating backwards to start this with the youngest jobs raises the chances to find some.
Only afterwards, it join()s with all sub-jobs which are now either, completed or currently processed by another worker thread.
Note that I changed the initiating code to use the default pool. This uses “number of CPU cores” minus one worker threads, plus the initiating thread, i.e. the main thread in this example.
Just a minor change is required.
You need to specify the parallelism for newWorkStealingPool as follows:
ForkJoinPool pool = (ForkJoinPool) Executors.newWorkStealingPool(5);
As per its documentation:
newWorkStealingPool(int parallelism) -> Creates a thread pool that maintains enough threads to support the given parallelism level, and may use multiple queues to reduce contention. The parallelism level corresponds to the maximum number of threads actively engaged in, or available to engage in, task processing. The actual number of threads may grow and shrink dynamically. A work-stealing pool makes no guarantees about the order in which submitted tasks are executed.
As per the attached Java Visual VM screenshot, this parallelism allows the program to work within the memory specified and never goes out of memory.
And, one more thing (not sure if it will make any effect):
Change the order in which fork is called and the task is added to list. That is, change
FileSearchRecursiveTask task = new FileSearchRecursiveTask(file.getAbsolutePath());
recursiveTasks.add(task);
task.fork();
to
FileSearchRecursiveTask task = new FileSearchRecursiveTask(file.getAbsolutePath());
task.fork();
recursiveTasks.add(task);
I have created some callable threads in a for loop, in my main method and started all of them.
After starting all threads I have Thread.sleep(10000). In this case what happens to my child callable threads.
psudocode:
public static void main(String[] args){
Map<String, String> columnNames_TypesMap = tableUtil.getColumnNamesAndTypesFromOracleDB(toTable);
ExecutorService executor = Executors.newFixedThreadPool(50);
Set<Future<String>> values = Collections.newSetFromMap(new ConcurrentHashMap<Future<String>, Boolean>());
Future<String> value = null;
boolean valueSizeLogged = false;
long preValuesSize = values.size();
outer: while (true) {
//In the below method I am reducing values size.
count = tableUtil.checkIfAllRowsCopied(values, count, totalNoOfRecordsInFromTable);
if (values.size() > 5) {
logger.info("****** Hence we do not create new threads. We do wait for the created threads to compelte");
Thread.sleep(sleepTime);//Here I am putting my main thread to sleep
continue outer;
}
for (int indexForThread = 1; indexForThread <= 15; indexForThread++) {
startingRange = endingRanges + 1;
endingRanges = endingRanges + maxNoOfRecordsPerThread;
Callable<String> callable2 = new InsertionCallable(as400SchemaName + "." + fromTable, startingRange, endingRanges, columnNames_TypesMap, toTable);
value = executor.submit(callable2);//Child Thread
values.add(value);
}
}
}
In the above code first i have created 15 Threads. in the second loop(outer while), I am doing Thread.sleep() if values size is greater than 5, at this point of time what will happen to the created child threads? Will Child threads also goes to Sleep mode or what happens to Child Threads.
Kindly help. Your help will be highly appreciable.
Thanks.
The child threads are not affected by the parent thread sleeping (or terminating, or pretty much anything else). They will happily chug along.
Is there any way I can get a list of all running threads in the current JVM (including the threads not started by my class)?
Is it also possible to get the Thread and Class objects of all threads in the list?
I want to be able to do this through code.
To get an iterable set:
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Performance: 0 ms for 12 threads (Azul JVM 16.0.1, Windows 10, Ryzen 5600X).
Get a handle to the root ThreadGroup, like this:
ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parentGroup;
while ((parentGroup = rootGroup.getParent()) != null) {
rootGroup = parentGroup;
}
Now, call the enumerate() function on the root group repeatedly. The second argument lets you get all threads, recursively:
Thread[] threads = new Thread[rootGroup.activeCount()];
while (rootGroup.enumerate(threads, true ) == threads.length) {
threads = new Thread[threads.length * 2];
}
Note how we call enumerate() repeatedly until the array is large enough to contain all entries.
Yes, take a look at getting a list of threads. Lots of examples on that page.
That's to do it programmatically. If you just want a list on Linux at least you can just use this command:
kill -3 processid
and the VM will do a thread dump to stdout.
You can get a lot of information about threads from the ThreadMXBean.
Call the static ManagementFactory.getThreadMXBean() method to get a reference to the MBean.
Have you taken a look at jconsole?
This will list all threads running for a particular Java process.
You can start jconsole from the JDK bin folder.
You can also get a full stack trace for all threads by hitting Ctrl+Break in Windows or by sending kill pid --QUIT in Linux.
You can try something like this:
Thread.getAllStackTraces().keySet().forEach((t) -> System.out.println(t.getName() + "\nIs Daemon " + t.isDaemon() + "\nIs Alive " + t.isAlive()));
and you can obviously get more thread characteristic if you need.
Apache Commons users can use ThreadUtils. The current implementation uses the walk the thread group approach previously outlined.
for (Thread t : ThreadUtils.getAllThreads()) {
System.out.println(t.getName() + ", " + t.isDaemon());
}
To get a list of threads and their full states using the terminal, you can use the command below:
jstack -l <PID>
Which <PID> is the id of process running on your computer. To get the process id of your java process you can simply run the jps command.
Also, you can analyze your thread dump that produced by jstack in TDAs (Thread Dump Analyzer) such fastthread or spotify thread analyzer tool.
In Groovy you can call private methods
// Get a snapshot of the list of all threads
Thread[] threads = Thread.getThreads()
In Java, you can invoke that method using reflection provided that security manager allows it.
Code snippet to get list of threads started by main thread:
import java.util.Set;
public class ThreadSet {
public static void main(String args[]) throws Exception{
Thread.currentThread().setName("ThreadSet");
for ( int i=0; i< 3; i++){
Thread t = new Thread(new MyThread());
t.setName("MyThread:"+i);
t.start();
}
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for ( Thread t : threadSet){
if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup()){
System.out.println("Thread :"+t+":"+"state:"+t.getState());
}
}
}
}
class MyThread implements Runnable{
public void run(){
try{
Thread.sleep(5000);
}catch(Exception err){
err.printStackTrace();
}
}
}
output:
Thread :Thread[MyThread:2,5,main]:state:TIMED_WAITING
Thread :Thread[MyThread:0,5,main]:state:TIMED_WAITING
Thread :Thread[MyThread:1,5,main]:state:TIMED_WAITING
Thread :Thread[ThreadSet,5,main]:state:RUNNABLE
If you need all threads including system threads, which have not been started by your program, remove below condition.
if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup())
Now output:
Thread :Thread[MyThread:2,5,main]:state:TIMED_WAITING
Thread :Thread[Reference Handler,10,system]:state:WAITING
Thread :Thread[MyThread:1,5,main]:state:TIMED_WAITING
Thread :Thread[ThreadSet,5,main]:state:RUNNABLE
Thread :Thread[MyThread:0,5,main]:state:TIMED_WAITING
Thread :Thread[Finalizer,8,system]:state:WAITING
Thread :Thread[Signal Dispatcher,9,system]:state:RUNNABLE
Thread :Thread[Attach Listener,5,system]:state:RUNNABLE
In the java console, hit Ctrl-Break. It will list all threads plus some information about the heap. This won't give you access to the objects of course. But it can be very helpful for debugging anyway.
public static void main(String[] args) {
// Walk up all the way to the root thread group
ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parent;
while ((parent = rootGroup.getParent()) != null) {
rootGroup = parent;
}
listThreads(rootGroup, "");
}
// List all threads and recursively list all subgroup
public static void listThreads(ThreadGroup group, String indent) {
System.out.println(indent + "Group[" + group.getName() +
":" + group.getClass()+"]");
int nt = group.activeCount();
Thread[] threads = new Thread[nt*2 + 10]; //nt is not accurate
nt = group.enumerate(threads, false);
// List every thread in the group
for (int i=0; i<nt; i++) {
Thread t = threads[i];
System.out.println(indent + " Thread[" + t.getName()
+ ":" + t.getClass() + "]");
}
// Recursively list all subgroups
int ng = group.activeGroupCount();
ThreadGroup[] groups = new ThreadGroup[ng*2 + 10];
ng = group.enumerate(groups, false);
for (int i=0; i<ng; i++) {
listThreads(groups[i], indent + " ");
}
}
You can use getAllThreadIds that Returns all live thread IDs. Some threads included in the returned array may have been terminated when this method returns.
ManagementFactory.getThreadMXBean().getAllThreadIds()
I`m trying to learn multithreading programming and I have some questions about the approach that would have to be taken.
So, in my specific case I want to build a program that renames 1000 files and I was thinking to create a worker class:
public class Worker implements Runnable {
private List<File> files ;
public Worker(List<File> f){
files = f;
}
public void run(){
// read all files from list and rename them
}
}
and then in main class to do something like:
Worker w1 = new Worker(..list of 500 files...) ;
Worker w2 = new Worker(..list of the other 500 files...) ;
Thread t1 = new Thread(w1,"thread1");
Thread t2 = new Thread(w2,"thread2");
t1.start();
t2.start();
Running this brings me no concurrency issues so I do not need synchronized code, but I`m not sure if this is the correct approach...?
Or should I create only one instance of Worker() and pass the entire 1000 files list, and the take care that no matter how many threads access the object thew won`t get the same File from the list ?
i.e :
Worker w1 = new Worker(..list of 1000 files...) ;
Thread t1 = new Thread(w1,"thread1");
Thread t2 = new Thread(w1,"thread2");
t1.start();
t2.start();
How should I proceed here ?
The First approach you said is correct one. You need to create two Worker as each worker will work on different list of file.
Worker w1 = new Worker(..list of 500 files...) ; // First List
Worker w2 = new Worker(..list of the other 500 files...) ; // Second List
Thread t1 = new Thread(w1,"thread1");
Thread t2 = new Thread(w2,"thread2");
t1.start();
t2.start();
It's simple here two different thread with load of 500 file will execute concurrently.
A more typical and scalable approach is one of the following:
create a collection (likely an array or list) of N threads to perform the work
use a thread pool, e.g. from Executors.newFixedThreadPool(N)
You may also wish to use a Producer Consumer pattern in which the threads pull from a common task pool. This allows natural balancing of the work - instead of essentially hard-coding one thread handles 500 tasks and the other the same number.
Consider after all what would happen if all of your larger files end up in the bucket handled by the Thread2? The first thread is done/idle and the second thread has to do all of the heavy lifting.
The producer/consumer pooling approach would be to dump all of the work (generated by the Producer's) into a task pool and then the Consumers (your worker threads) bite off small pieces (e.g. one file) at a time. This approach leads to keeping both threads occupied for a similar duration.
In learning multi-threaded programming one of the important insights is that a thread is not a task. By giving a thread a part of the list of items to process you are halfway there but the next step will take you further: constructing the task in such a way that any number of threads can execute it. To do this, you will have to get familiar with the java.util.concurrent classes. These are useful tools to help constructing the tasks.
The example below separates tasks from threads. It uses AtomicInteger to ensure each thread picks a unique task and it uses CountDownLatch to know when all work is done. The example also shows balancing: threads that execute tasks that complete faster, execute more tasks.
The example is by no means the only solution - there are other ways of doing this that could be faster, easier, better to maintain, etc..
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class MultiRename implements Runnable {
public static void main(String[] args) {
final int numberOfFnames = 50;
MultiRenameParams params = new MultiRenameParams();
params.fnameList = new ArrayList<String>();
for (int i = 0; i < numberOfFnames; i++) {
params.fnameList.add("fname " + i);
}
params.fnameListIndex = new AtomicInteger();
final int numberOfThreads = 3;
params.allDone = new CountDownLatch(numberOfThreads);
ExecutorService tp = Executors.newCachedThreadPool();
System.out.println("Starting");
for (int i = 0; i < numberOfThreads; i++) {
tp.execute(new MultiRename(params, i));
}
try { params.allDone.await(); } catch (Exception e) {
e.printStackTrace();
}
tp.shutdownNow();
System.out.println("Finished");
}
private final MultiRenameParams params;
private final Random random = new Random();
// Just to show there are fast and slow tasks.
// Thread with lowest delay should get most tasks done.
private final int delay;
public MultiRename(MultiRenameParams params, int delay) {
this.params = params;
this.delay = delay;
}
#Override
public void run() {
final int maxIndex = params.fnameList.size();
int i = 0;
int count = 0;
while ((i = params.fnameListIndex.getAndIncrement()) < maxIndex) {
String fname = params.fnameList.get(i);
long sleepTimeMs = random.nextInt(10) + delay;
System.out.println(Thread.currentThread().getName() + " renaming " + fname + " for " + sleepTimeMs + " ms.");
try { Thread.sleep(sleepTimeMs); } catch (Exception e) {
e.printStackTrace();
break;
}
count++;
}
System.out.println(Thread.currentThread().getName() + " done, renamed " + count + " files.");
params.allDone.countDown();
}
static class MultiRenameParams {
List<String> fnameList;
AtomicInteger fnameListIndex;
CountDownLatch allDone;
}
}