Jake
Jake

Reputation: 143

Specific issue with java multithreading

I'm running into a specific problem with java threads and am not sure how to solve it.

The following code is part of a larger project. It's supposed to loop through a list of strings (e.g. >1000 entries). For every string, the main thread should create a new thread that immediately starts analyzing the current string. If more than 4 of these worker threads are active concurrently, the main thread should wait() until notified by a finishing thread, then continue these instructions until all strings have looped over.

After leaving the for-loop, the main thread has to wait until all remaining worker threads finish. This is done to prevent the main thread from closing a necessary database connection further down the line in the project.

public void solveProblem() {

final int MAX_THREAD_COUNT = 4;                     //sets the maximum amount of concurrent threads
final Integer threadCount = new Integer(0);         //keeps count of running threads

Object mainLock = new Object();                     //used to lock main thread
final Lock threadLock = new ReentrantLock();        //used to lock subthreads
int threadID = 0;                                   //identifies threads, used for debugging

for (String s : strings) {

    class AnalyzeThread implements Runnable {

        private String string;
        private Integer threadCount;
        private Lock innerLock;
        private Object mainLock;
        private int threadID; //used for debugging, delete when this class works

        public AnalyzeThread(String string, Integer threadCount, Lock innerLock, Object mainLock, int threadID) {
            this.string = string;
            this.threadCount = threadCount;
            this.innerLock = innerLock;
            this.mainLock = mainLock;
            this.threadID = threadID;
            System.out.println("THREAD "+threadID+": Launched");
        }

        @Override
        public void run() {

            innerLock.lock();
            threadCount++;
            innerLock.unlock();

            try {
                <thread analyses current for-loop's string. this takes around 1 second>
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            innerLock.lock();
            threadCount--;
            innerLock.unlock();

            synchronized(mainLock) {
                notifyAll();
            }
        }
    }


    synchronized (mainLock) {
        System.out.println("Main thread: Creating and starting thread #"+threadID);
        AnalyzeThread r = new AnalyzeThread(string, threadCount, threadLock, mainLock, threadID++);
        Thread t = new Thread(r);
        t.start();
    }

    // In order to prevent too many simultaneous threads from popping up, we'll
    // suspend the main thread should there be more than MAX_THREAD_COUNT threads.
    synchronized (mainLock) {
        System.out.println("Main thread: Checking thread count: Count = "+threadCount);
        while(threadCount > MAX_THREAD_COUNT) {
            try { 
                System.out.println("Main thread detects more than 4 threads. sleeping.");
                mainLock.wait();
            } catch(InterruptedException e) {
                System.out.println("Main thread wakes after a notify.");
                continue;
            }
        }
    }
}

// After all files have been sent to threads, we'll have to wait for all threads to finish
// their jobs before exiting.
synchronized (mainLock) {
    System.out.println("Main thread exited for-loop. Waiting for threads to close. Running threads: "+threadCount);
    while(threadCount > 0) {
        try { 
            System.out.println("Main thread after execution: More than 0 threads are running. Sleeping.");
            mainLock.wait();
        } catch(InterruptedException e) {
            System.out.println("Main thread after execution: 0 active threads. Exiting.");
            continue;
        }
    }
}

System.out.println("-------------Main thread exiting.----------");
}

Executing this program seems to result in the main thread continuously spawning subthreads while running the rest of the program at the same time, i.e. the aforementioned database connection ends up being closed but the for-loop in the above code snippet continues spawning threads and analyzing strings.

This class is the only instance in the entire project where threads are used, and based on tangential experiences with pthreads/mutexes in C I figured it would run correctly. Did somebody spot an issue, or does this code run correctly and the problem must lie somewhere else?

Upvotes: 0

Views: 86

Answers (4)

Sahil
Sahil

Reputation: 96

In your case your main thread is only running then why you have put the synchronization in your code and I also want to tell you that in your case Five child thread will run instead of four.Also one point you have to note that you have use the same lock for the parent as well as child so it looks weird.

Also change your code like,

 synchronized(mainLock) {
                threadCount++;                   
            }
  <your Thread code will come here..>
 synchronized(mainLock) {
                threadCount--;                   
                notifyAll();
            }

Because,if you put outside the mainLock then everytime main thread will find threadCount less than 4 and your child thread is not able to get the mainLock as it is already occupy by the main thread.

Upvotes: 0

Mark
Mark

Reputation: 76

The value of threadCount is passed-by-value to the new Thread. Hence the thread will only increment its own threadCount variable.

A simple solution would be to give the new thread a reference the its own object and create methods that increment and decrement the threadCounter.

Edit: I missed that the integer was stored in a class, which might invalidate my reasoning. However, I still think that all threads are seeing local copies, which would mean that my solution should work.

Upvotes: 2

Solomon Slow
Solomon Slow

Reputation: 27115

As Mark said, you have two different threadCount variables, that's your problem.

But there are cleaner ways to do it. One would be to use a Semaphore that is initialized to 4. Have the main thread decrement the semaphore each time it starts a task, and have your threads increment it each time they finish.

Better still: Since your program is executing many tasks, why not use a ThreadPoolExecutor with four threads to execute your tasks?

Upvotes: 2

Aries7
Aries7

Reputation: 46

i think your problem stems from

final Integer threadCount = new Integer(0);

its final, so it cannot be changed. so your main function will always evaluate it as 0. the instance of threadCount inside the class can change, but that is not the one your while loops are looking at. At least i THINK that is what is going on here.

Upvotes: 0

Related Questions