【问题标题】:Should implementations of Runnable allow for concurrent calls to run() on the same instance?Runnable 的实现是否应该允许在同一实例上同时调用 run()?
【发布时间】:2012-05-22 15:09:30
【问题描述】:

如果我有一个实现java.lang.Runnable 的类,从技术上讲,没有什么可以阻止多个线程在Runnable相同 实例上同时调用run。示例:

Runnable r = new MyRunnable();

//start thread 1
new Thread(r).start();

//start thread 2
new Thread(r).start();

如果run 方法完全是“自包含的”(即不依赖任何私有实例成员),这并不是真正的问题,因为线程之间没有共享变量,所以你的Runnable 可以永远不会得到“损坏”的内部状态。但我认为在大多数情况下,您的Runnable 会在run 中操作一些私有实例成员,那么该怎么办?

举个例子,我的MyRunnable 类有一个私有的mode 字段。主要目的是彻底停止run 方法(通过将mode 设置为STOPPED),但它还有其他用途。

public static class MyRunnable implements Runnable
{
    enum Mode { RUNNING, PAUSED, STOPPED, FASTFORWARDING, REWINDING; }

    private volatile Mode mode;

    @Override
    public void run() 
    {
        mode = Mode.RUNNING;
        while (mode != Mode.STOPPED)
        {
            //do stuff. possibly other changes to the mode.
        }
    }

    public void setMode(Mode mode)
    {
        this.mode = mode;
    }

    public Mode getMode()
    {
        return mode;
    }
}

如您所见,如果我为MyRunnable相同 实例同时执行多个线程(如前所示),对mode 的任何更改都可能以不同方式影响并发运行的线程.例如,如果一个线程将mode 设置为STOPPED,则另一个线程可能会在所有其他线程看到它被设置为STOPPED 之前将mode 更改为其他值。这可以很容易地阻止线程退出 while 循环。

那么我如何将Runnable 设计为“同时可用”? 甚至可能吗?不值得吗?我可以使用 C# 的 ThreadStatic 属性(我认为在这种情况下会很完美)吗?

【问题讨论】:

    标签: java multithreading concurrency software-design runnable


    【解决方案1】:

    在这种情况下,构建MyRunnable 的多个实例比保护它们免受多次使用更为常见:

    //start thread 1
    new Thread(new MyRunnable()).start();
    //start thread 2
    new Thread(new MyRunnable()).start();
    

    让多个线程工作的全部目的是为了实现良好的并行计算。如果线程需要锁定公共资源(如工作队列等),那很好,但写你的Runnables 以使它们“同时可用”不应该作为理所当然的事情 - 应该有充分的理由这样做所以。

    有可能吗?

    当然可以。线程之间一直共享资源。但是,在您使用 Mode mode 变量的情况下,这应该通过您的 Runnable 类的多个实例来完成。

    如果多个线程正在调用公共代码,您可能会考虑使用ThreadLocal 来存储每个线程的信息。但是,将字段存储在 MyRunnable 的多个实例中会比将一个 MyRunnable 实例与 mode 存储在 ThreadLocal 中更有效。

    不值得吗?

    如果原则上这样做,那肯定是值得的。同样,分叉线程的全部意义在于让它们与其他线程尽可能分开以利用多个处理器。

    【讨论】:

      【解决方案2】:

      与您将任何其他方法或类设计为线程安全的方式相同。 Runnable 在本质上没有什么不同。

      C# ThreadStatic 等效于 ThreadLocalVariable,但是这是否是适当的解决方案是任何人的猜测,因为我们无法知道您希望您的班级遵循什么行为。

      如果您想有效地让每个线程运行独立执行您的 Runnable(关于状态),那么拥有不同的实例会更有意义(并且更简单)。

      【讨论】:

        【解决方案3】:

        总的来说,这是不值得的。

        无论如何,创建Runnable 的新实例的成本不能太高。如果是,您必须考虑如何排除初始化成本高昂的部分,并确保它们是线程安全的。

        您当然也可以使用ThreadLocal 变量,总体而言它非常有用,但在这种情况下可能不是。

        【讨论】:

          【解决方案4】:

          好吧,我不能说我对 Java 中的 MP 有很多经验,所以我不会尝试谈论常见用法,但是在阅读了 RunnableThread 文档之后,听起来 Runnable 是意在顾名思义:一个可运行的对象。具体来说,文档提到在您不希望实现 Thread 但希望使用类似概念的情况下使用 Runnable。我实际上认为这使得 Runnable 非常适合封装并行或并发算法的类。当然,没有什么暗示您不应该或不能专门设计 Runnable 以实现并发性。当然,这本身(取决于任务)远不止一个 SO 问题可以解决的问题。

          编辑:我应该澄清一下,我提到了在 Runnable 中封装并行或并发算法,但我的意思是说,在 Runnable 中封装任务也可能有意义,然后让调用者决定是否分配并行资源例如,通过在并发线程中调用它来执行该任务。我的观点是,正如文档所暗示的那样,如果您可以想象一个正在运行的对象(在任何情况下),那么 Runnable 可能是一个不错的选择!

          【讨论】:

            【解决方案5】:

            我认为 OP 的问题是在某些情况下,当 Runnable 资源繁重时,有没有办法避免重新创建这些 Runnables?

            我的经验是,您总是可以创建轻量级 Runnable 来引用重量级资源(例如巨大的数组等)。避免在多个线程上调用相同的 Runnable,但在我看来,为每个线程创建不同的 Runnable 是一种更好的做法。

            【讨论】:

              猜你喜欢
              • 2012-02-25
              • 1970-01-01
              • 2023-04-02
              • 2014-01-25
              • 1970-01-01
              • 1970-01-01
              • 2014-11-16
              • 1970-01-01
              • 2012-12-20
              相关资源
              最近更新 更多