Java如何从单独的线程获取调用者?

时间:2014-09-09 03:40:47

标签: java multithreading

嗯...不确定这是否可行,但是如果你在一个单独的线程上运行,那么如何在Java中获取真正的调用者的类名(理想情况下也是方法名)?我想将类名称卸载到一个单独的线程(以便在我每秒执行100多次日志记录操作时不阻止UI线程。)

例如:

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" ?
    }
}

用例是这样的:我记录了大量数据,而我根本不想阻止主线程。一些日志记录可能是快速和小数据,但有些可能会记录转储很多东西。问题还在于,现在,编写代码的方式,callerMethod()大约有600多个入口点,因此重构将是一个相当大的挑战。

可替换地:

如果你能证明Thread.currentThread().getStackTrace()[2].getMethodName();每次保证是一个小于5毫秒的恒定时间操作,那么在主线程上这是可以接受的。

3 个答案:

答案 0 :(得分:4)

编辑:

好的,你想要避免堆栈跟踪。我环顾了一下:确定调用者的复杂性实际上在LogRecord。如果您可以通过Logger.setSourceClassName()(根本不是任何字符串)手动设置调用者,则LogRecord将不再构建堆栈跟踪以查找调用者的名称。

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 );
   }
}

原始答案:

子类化Thread会起作用,但我有点怀疑你为什么要这样做。对于调试可能,但这是关于我能想到的唯一用例。 (P.S.我必须在堆栈跟踪中更改您的偏移量。" 2"将获得callerMethod的调用者 - " main"在下面的示例中。)

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();
}

答案 1 :(得分:0)

    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() );
        }
    }
}

答案 2 :(得分:0)

有两种情况。

  1. 您扩展Thread.如果您覆盖Thread.start(),,则可以在那里查看堆栈跟踪。
  2. 您实施了Runnable.在这种情况下,原则上您无法了解。你可以知道的是,谁通过在构造函数中查看堆栈跟踪来创建Runnable,,但这不一定是启动线程的相同方法。