Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java (Android) multi-threading process

I am working on application (Matt's traceroute windows version http://winmtr.net/) which creates multi threads each thread has its own process (which execute ping command). ThreadPoolExecutor shutdown all threads after some time( e.g.10 seconds)

ThreadPoolExecutor uses blocking queue(holding tasks before they executed)

int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
    NUMBER_OF_CORES * 2, NUMBER_OF_CORES * 2 + 2, 10L, TimeUnit.SECONDS, 
    new LinkedBlockingQueue<Runnable>()
);

PingThread.java

private class PingThread extends Thread {

    @Override
    public void run() {
        long pingStartedAt = System.currentTimeMillis();
        // PingRequest is custom object
        PingRequest request = buildPingRequest(params);

        if (!isCancelled() && !Thread.currentThread().isInterrupted()) {

            // PingResponse is custom object

            // Note:
            // executePingRequest uses PingRequest to create a command 
            // which than create a runtime process to execute ping command
            // using string response i am creating PingResponse

            PingResponse pingResponse = PingUtils.executePingRequest(request);

            if (pingResponse != null) {
                pingResponse.setHopLocation(hopLocation);                   
                // publish ping response to main GUI/handler
                publishProgress(pingResponse);
            } else
                Logger.error(
                    "PingThread", "PingResponse isNull for " + request.toString()
                );
        }
    }
}

Now if i create multiple threads say more than 500 in a loop and execute inside pool executor

Executing Threads

PingThread thread = new PingThread(params);
poolExecutor.execute(thread);

I do know that LinkedBlockingQueue holds tasks before they executed. Each thread's process takes maximum 200 to 400ms but generally it is less than 10ms

What i am doing

for (int iteration = 1; iteration <= 50/*configurable*/; iteration++) {

    for (int index = 0; index < 10/*configurable*/; index++) {
        PingThread thread = new PingThread(someParams);
        poolExecutor.execute(thread);
    }

    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        Logger.error(false, e);
    }   
}

50 iterations will take about 25 seconds, here i only have up-to 40 ping responses rest consider as loss due to time out. If i increase iterations loss increases too (exponentially due to increase in no of threads)

Observation:

I am running this application on Galaxy S6 which has 8 cores, application pool size is 16 and maximum pool size is 16 + 2, i do know that processor runs only one thread at a time, it shares a quantum time for parallel processing.

By observing ThreadPoolExecutor on timely basis, i see many tasks in queue, after timeout there are still many threads present in queue due to LinkedBlockingQueue

If i decrease no of threads it works fine but if increase it creates problem

Problem:

  • Ping responses decreases when i use devices with dual core processor.
  • Why there are many threads present in queue, where each thread takes about 10 to 50ms (increase threads time will increase uptp 300ms or more)?
  • It should complete with in given time, why its not?
  • How to overcome this problem?
  • Should i use ConcurrentLinkedQueue but it uses Producer/consumer model, somehow ThreadPoolExecutor (i think it is) uses this model too.
  • LinkedBlockingQueue holds tasks before they executed (threads are idle or in queue), how to overcome this?
  • By setting Thread.MAX_PRIORITY for latter iterations does't solve the problem (later iteration's thread are in queue)
  • Decreasing no of threads solves the problem why? because there are less no threads present in queue?
  • Is there any way to check, if threads present in queue entertain them then execute other, without blocking other threads but within given time.
  • Adding extra time like 5 seconds is not a solution
  • Changing corePoolSize like in How to get the ThreadPoolExecutor to increase threads to max before queueing? is not working in my case.

During testing Memory and Processor usage are with in a limit.

Detail answer/help is required.

Edit

When application goes into background there is no loss and user CPU usage drops to 0-2% while on focus app took 4-6% of cpu usage. Is it due to UI and other ralted stuff, i tried to remove all unnecessary code also i did change PingThread to PingTask

PingTask implements Runnable {/*....*/}

Note: I created separate java based application using same code and it works fine on desktop, so can we say it's android OS specific issue?

like image 907
Haris Qurashi Avatar asked Nov 25 '16 16:11

Haris Qurashi


1 Answers

I am not sure if this is what causes all the problems, but you are creating a lot of unnecessary threads.

You shoud replace

private class PingThread extends Thread {

with :

private class PingThread implements Runnable {

or (using a more adequate name) :

private class PingTask implements Runnable {

i.e. the tasks submited to Executors are not supposed to be thread themselves. It works, because a Thread implements Runnable, but you are wasting it.

like image 56
bwt Avatar answered Oct 11 '22 23:10

bwt