简体   繁体   中英

Java how to get caller from separate thread?

Hmm... not sure if this is possible, but how would one go about grabbing the true caller's class name (and ideally also method name) in Java if you are running on a separate thread? I want to offload the class name fetching to a separate thread (so as not to block the UI thread when i'm doing like 100+ logging operations per second).

for example:

class Main {
    public void callerMethod() {
        Thread.currentThread().getStackTrace()[2].getMethodName(); // This gets me the caller. However, this is expensive to do on the UI Thread 100+ times per second. a call here can take up 70ms

        new Thread(new Runnable() {
            @Override
            public void run() {
                new SecondObject().iWantToKnowMyCaller();
            }
        }).start();
    }
}



class SecondObject {
    public void iWantToKnowMyCaller() {
        // how do i get the caller method here so that it's "callerMethod" from "Main" ?
    }
}

The use case is this: i'm logging a lot of data, and i don't want to block the main thread at all. some of the logging might be quick and small data, but some might log dump a lot of stuff. the problem also is that right now, the way the code is written, there's about 600+ entry points into callerMethod() , so refactoring is going to be quite a challenge.

ALTERNATIVELY:

if you can prove that Thread.currentThread().getStackTrace()[2].getMethodName(); is guaranteed to be a constant time operation less than like 5 ms each time, then that is acceptable to be on the main thread.

EDIT:

OK, you want to AVOID the stack trace. I looked around a bit: the complexity of determining the caller is actually in LogRecord . If you could manually set the caller via Logger.setSourceClassName() (to any string at all), then the LogRecord would no longer build a stack trace to look for the name of the caller.

public class ThreadTest
{
   public static void main( String[] args )
   {
      LogRecord lr = new LogRecord( Level.INFO, "Hi" );
      lr.setSourceClassName( "ThreadTest.main" );  // anything, including null
      Logger.getAnonymousLogger().log( lr );
   }
}

Original answer:

Subclassing Thread will work, but I kind of question why you'd want to do that. For debugging maybe, but that's about the only use case I can think of. (PS I had to change your offset in the stack trace. "2" will get the caller of callerMethod -- "main" in the example below.)

public class ThreadTest
{
   public static void main( String[] args )
   {
      new Main().callerMethod();
   }
}

class Main {
    public void callerMethod() {
        final String callee = Thread.currentThread().getStackTrace()[1].getMethodName(); // This gets me the caller
        new MyThread(new Runnable() {
            @Override
            public void run() {
                new SecondObject().iWantToKnowMyCaller();
            }
        }){
        @Override
        public String getInvoker() { return callee; }}.start();
    }
}

abstract class MyThread extends Thread implements Invoker {    
   public MyThread( Runnable r )
   {
      super( r );
   }
}

class SecondObject {
    public void iWantToKnowMyCaller() {
        // how do i get the caller method here so that it's "callerMethod" from "Main" ?
       System.out.println( ((MyThread)(Thread.currentThread())).getInvoker() );
    }
}

interface Invoker {
   String getInvoker();
}
    public class ThreadTest{
        public static void main(String[] args) {
            new Main().callerMethod();
        }
    }
class Main{
    public void callerMethod() {
        new Thread(new MyRunnable(new Throwable())).start();
    }
    class MyRunnable implements Runnable {

        Throwable t;

        MyRunnable(Throwable t) {
            this.t = t;
        }

        @Override
        public void run() {
            StackTraceElement[] ses = t.getStackTrace();
            System.out.println(ses[0].getClassName() + ":" + ses[0].getMethodName() );
        }
    }
}

There are two cases.

  1. You extended Thread. If you override Thread.start(), you can peek the stack trace there.
  2. You implemented Runnable. In this case you can't know, in principle. All you can know is who created the Runnable, by peeking the stack trace in the constructor, but that isn't necessarily the same method that started the thread.

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