Java:如何在“Runnable”类中私有调用“run”方法?

3
我正在编写一个涉及多线程工作池的Java程序,其中包含一些Process。每个Process实例都需要能够生成一个单独的附加线程来执行一些工作。但是该线程应该由该实例本身而不是其他人生成。不幸的是,Runnable.run是“public”方法,所以我不能真正地在没有进行某些技巧的情况下强制执行这一点。
以下是我计划使用的技巧:
1. 在Process中实现Runnable 2. 将此代码写入实现中:
代码:
 class Process implements Runnable {

    private boolean threadkey = false;

    public void run() {

        synchronized(threadkey) {

            // is someone wrongly calling 'run'?
            if(!threadkey)
                return;

            /* switch off threadkey to make sure
            it cannot be called meaningfully again */
            threadkey = false;
        }
        /* continue processing
         *
         */
        return;
    }

当我想要合法地运行时,现在我所需要做的就是在调用之前打开“threadkey”(它是私有的)。
优雅吗?还是不优雅?或者有更好的方法?或者我应该不费心地解决这个问题,并写一个简洁明了的注释来解释不要调用“run”?
人们是否会从需要“运行”的类中调用“run”?
4个回答

12
虽然您说得没错,一个Runnable的run方法是公共的,但可以采取一种方法来防止其他人调用它,那就是把整个实现Runnable的类设为包级私有或私有内部类。这样,尽管它可能具有public的run方法,除了您自定义的类之外的代码将无法实例化该Runnable对象。如果您的类不返回对其的引用,则客户端将无法访问该Runnable,因此他们无法调用run方法。也就是说,不要让您的Process类实现Runnable;相反,创建另一个实际实现Runnable的类,然后使Process成为唯一能够访问它的类。
这种方法比您提出的更优雅,因为它在编译时而不是运行时阻止其他代码调用run。特别是,如果任何代码尝试调用上述的run方法,它会顺利编译,但在运行时会失败。问题是代码会编译但永远不会正确工作。使Runnable不可访问意味着如果有人确实尝试运行它,他们会得到编译时错误,并且必须重新考虑他们的设计。换句话说,编译器将在错误发生之前检测到该错误。
一般来说,如果您想防止随机代码调用类的某些必须是公共的方法,因为它们在接口中声明,请考虑更改整个类的访问限定符,以便客户端无法引用它。

7
您可以像这样使用 Runnable 的匿名内部类:
private void spawnThread() {
    Thread t = new Thread( new Runnable(){
          public void run(){
             // do something
          }
    }
    t.start();
}

spawnThread()只能在类内部调用,因为它是私有的。


1

你可以将run()方法设为私有,并使用一个内部类(实现Runnable)来访问它:

public class Process {

  private Thread extraThread = null;

  private void run() { 
      // Do you thing
  }

  public synchronized void spawnExtraThread() {
    if(extraThread != null)  
      return; // Do nothing an extra thread was already spwaned
    extraThread = new Thread() {
      public void run() { Process.this.run(); }
    };
    extraThread.start();
  }
}

主要的问题是Process类不再实现Runnable接口,因此不能再转换为线程。当然,你可以扩展这个设计来支持对线程数量的任何限制(只需将extraThread字段改为一个线程数组)。

0

我只是想要加入我的两分钱,这里有一个优雅的解决方案,使用Executors双冒号运算符(::),这还没有被提到:

import java.util.concurrent.Executors;

public class PrintFoo {

    public void run() {
        Executors.newSingleThreadExecutor().execute(this::doRun);
    }

    private void doRun() {
        for (int i = 20; i --> 0;) System.out.println("Foo");
    }
}

然后调用它的类:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        new PrintFoo().run();
        System.out.println("Going to sleep...");
        Thread.sleep(10000L);
    }
}

输出:

Going to sleep...
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo
Foo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接