1

如果java.lang.Runnable有一个run实现. 例子:Runnable

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方法(通过设置modeSTOPPED),但它也有其他用途。

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可能以不同的方式影响并发运行的线程。例如,如果一个线程将 设置modeSTOPPED,则另一个线程可能会mode在每个其他线程看到它被设置为 之前将其更改为其他值STOPPED。这可以很容易地阻止线程退出 while 循环。

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

4

5 回答 5

9

MyRunnable在这种情况下构造您的多个实例而不是保护它们免受多次使用更为常见:

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

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

甚至可能吗?

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

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

不值得吗?

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

于 2012-05-22T15:14:12.017 回答
1

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

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

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

于 2012-05-22T15:20:16.323 回答
0

一般来说,这是不值得的。

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

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

于 2012-05-22T15:13:59.167 回答
0

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

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

于 2012-05-22T15:21:16.600 回答
0

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

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

于 2012-05-22T15:24:20.217 回答