简体   繁体   中英

Alternative to long, non-interrumpible method in a java/android thread

In the UI Thread in an Android application, I need to run a method which can take nanoseconds or forever to complete. I think I cannot exactly determine when it'll take forever, so I'm running it in a separate thread through a pool.

myPool = Executors.newCachedThreadPool(new ThreadFactory(){
    //override newThread(Runnable r)...
});

Future<Result> futureResult = myPool.submit(new Callable<Result>(){
    @Override public Result call(){
        return dangerousMethod();
    }
});

For demonstrating purposes, I block the UI for a second and then, if the method hasn't finished, I move on.

try{
    Result result = futureResult.get(1, TimeUnit.SECONDS);
    //Use result here
}catch(TimeoutException e) {//Other Exceptions are ommited
    futureResult.cancel(true);
    Log.d("DEMO", "Method takes too long. Skip");
}

I try to cancel the future and interrupt its thread with futureResult.cancel(true) , but the dangerousMethod() won't respond to interruptions, so the thread will keep running until it eventually completes its task.

What I've tried: Subclassing a ThreadPoolExecutor and get the private Thread s through reflection ( ThreadPoolExecutor$Worker.thread ) to force (Bad Practices TM) either a stop() (not working because UnsupportedOperationException ) or a stop0() (also non-working because NoSuchMethodException ). More details on this:

class MyPool extends ThreadPoolExecutor{
    //constructor, reflection code to get the Threads, etc

    void cancelThread(Thread thread){
        try {
            thread.stop();
        }catch(SecurityException | UnsupportedOperationException e) {
            try {
                Method stopMethod = Thread.class.getDeclaredMethod("stop0", Object.class);
                stopMethod.setAccessible(true);
                stopMethod.invoke(thread, new ThreadDeath());
            }catch(NoSuchMethodException | 
                   InvocationTargetException | 
                   IllegalAccessException x)
            {
                Log.d("MyPool", "Can't stop thread", x);
            }
        }
    }
}

The dangerous method is actually like this:

BigDecimal n1 = new BigDecimal("1E1000000");
BigDecimal n2 = new BigDecimal("1");
BigDecimal result = n1.add(n2);//Takes too long

I could check every BigDecimal method I use for the numbers it handles well, but I'd first like to know wether I'm missing something obvious or there is another approach. Because

BigDecimal n3 = new BigDecimal("1E999999");
BigDecimal result = n1.add(n3);

works well.

It seems that you cannot forcefully kill a Java thread in Android - in a Oracle Java it actually still works. The reason is that in Oracle Java stop() calls stop1() , while on Android it calls stop(new ThreadDeath()) which in turn unconditionally throws UnsupportedOperationException .

But why bother forcing it to stop? Just let it run in the background, forget it and go on. Sure you have some risk of bogging memory and CPU, but as you said only some of the numers cause the method to run that long, so chances are that you can forget this very thread and be able to succesfully start most of others.

If the long running background thread tries to do actions after being finished you can just set a flag telling him to shut up. As long as you don't need its result you are good.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM