0

不确定我是否正确理解线程,有人可以告诉我在以下示例中我是对还是错:

class Task {
String taskName;
private Thread thread;
boolean isFinished;

public Task(String name){
    taskName = name;
}

public void createTask(final Runnable r) {
    thread = new Thread(r){
        public void run(){
            if(r != null) {
                r.run();
                isFinished = true;
            }
        }
    };
    thread.start();
}
}

我在我的应用程序中实际做的是我将 设置isFinishedtrue,并有一个观察者,只要它isFinished为真,就会做一些事情。恐怕在我作为参数传递isFinished的所有代码实际终止之前设置为true 。Runnable

运行方法不是假设将我传递的代码放在单独的线程中并异步运行该代码吗?

4

7 回答 7

2

关闭,但您的新线程已经被赋予了要执行的可运行对象。你真的想给它一个运行 r.run() 方法然后设置 isFinished 的包装器。

改变:

public void createTask(final Runnable r) {
    thread = new Thread(r){
        public void run(){
            if(r != null) {
                r.run();
                isFinished = true;
            }
        }
    };
    thread.start();
}

public void createTask(final Runnable r) {
    thread = new Thread( new Runnable {
        public void run(){
            if(r != null) {
                r.run();
                isFinished = true;
            }
        }
    });
    thread.start();
}

如果我没有指出 isFinished 的线程不安全性,那我就失职了。如果不添加同步,您将无法保证在线程完成时注意到。我建议您添加:

public synchronized boolean getIsFinished()
{
    return isFinished;
}

public synchronized void setIsFinished(boolean finished)
{
    isFinished = finished;
}

并使用这些方法来获取或设置 isFinished 标志。鉴于您在这里缺乏同步,您可能会看到其他线程安全异常,这取决于您的 r.run() 方法和您的其他“观察者”是否也在没有同步的情况下共享数据。

于 2013-01-11T21:16:25.767 回答
1

您几乎不应该将 a 传递给 aRunnable的构造函数Thread 覆盖 Thread 的run()方法。

以下两段代码本质上是相同的:

Runnable r = new Runnable( )
{
    public void run( )
    {
        // do stuff...
    }
};

new Thread( r ).start( );

这是通过覆盖来完成同样事情的另一种方法run()

(new Thread( )
{
    public void run( )
    {
        // do stuff...
    }
}).start( );
于 2013-01-11T21:17:17.563 回答
0

好吧,您的代码部分正确,部分错误。

您是正确的,isFinished只有在您传入参数的可运行对象中的所有内容都完成执行后,才会设置为 true。

但是,由于 java 内存模型的特殊语义(我将在下面详细介绍),当您设置isFinished为 true 时,该更改可能仅对已将该变量设置为 true 的线程可见. 如果您希望代码按预期工作,则需要声明isFinished为 volatile。这将使您对该变量所做的任何更改立即被其他线程看到。

另一种方法是声明isFinished为 AtomicBoolean 而不是布尔值。这个类有很多方法可以让你以原子的方式检查和设置布尔值,帮助你避免许多常见的多线程陷阱。

于 2013-01-11T21:16:16.880 回答
0

您编写代码的方式在完成isFinished之前不会设置为 true r.run()。它可能会以其他方式出现,因为由于缺少同步或缺少易失性声明,您可能会遇到一些数据可见性问题。

这有点奇怪,因为您都将 Runnable 传递给构造函数,但是使用方法声明中的引用而不是线程内部的引用来调用它。但它“有效”,那里只是冗余。

顺便说一句,不要忘记@Override在您的匿名课程中:)

于 2013-01-11T21:16:44.503 回答
0

不,run 方法只是一个普通函数,您可以在扩展 Thread 类时重写它以实现您自己的行为。

它是 Thread 类的 start 方法,它启动一个新线程并异步运行该代码。

于 2013-01-11T21:15:54.307 回答
0

我建议您使用专门为您的问题设计的同步原语。

这个原语称为CountDownLatch

这是更新的代码:

class Task {

  String taskName;
  private Thread thread;
  CountDownLatch finishedSignal = new CountDownLatch( 1 );

  public Task(String name){
    taskName = name;
  }

  public void createTask(final Runnable r) {
    thread = new Thread(r){
        public void run(){
            if(r != null) {
                r.run();
                finishedSignal.countDown( );
            }
        }
    };
    thread.start();

    finishedSignal.await( );
  }
}
于 2013-01-11T21:26:18.827 回答
0

您应该使用FutureTask而不是您自己的 Task 类。它有一个 isDone() 方法,并且很好地与 Executor 框架集成。

最重要的是,happens-before关系按照您的预期维护(实际上在您的代码中,问题不在于 isFinished 设置为 true,在 Runnable 中的所有代码终止之前,而是另一种方式:它可能不会设置为即使 Runnable 被终止,在原始线程中为 true)

例子:

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("work done");
    }
};

FutureTask<Void> task = new FutureTask<Void>(runnable, null);
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit (task);

while (!task.isDone()) {
    System.out.println("waiting...");
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
于 2013-01-11T21:30:07.330 回答