Java 可运行/线程可能的误解

Java 可运行/线程可能的误解,java,concurrency,Java,Concurrency,不确定我是否正确理解线程,有人能告诉我以下示例中我是对的还是错的: 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 r

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

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();
}
}
我在我的应用程序中实际做的是,我将
isFinished
设置为
true
,并让一个观察者在
isFinished
为true时执行一些操作。很抱歉,
isFinished
Runnable
I pass as参数中的所有代码实际终止之前设置为true


run方法不是应该将我传递的代码放在一个单独的线程中,并异步运行该代码吗?

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


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

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


Thread类的start方法启动新线程并异步运行代码。

您的代码部分正确,部分错误

正确的说法是,
isFinished
只有在传入参数的runnable中的所有内容都完成执行后才会设置为true

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


另一种方法是将
isFinished
声明为原子布尔而不是布尔。该类有许多方法,允许您以原子方式检查和设置布尔值,帮助您避免许多常见的多线程陷阱。

您的代码部分正确,部分错误

正确的说法是,
isFinished
只有在传入参数的runnable中的所有内容都完成执行后才会设置为true

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


另一种方法是将
isFinished
声明为原子布尔而不是布尔。该类有许多方法,允许您以原子方式检查和设置布尔值,帮助您避免许多常见的多线程陷阱。

Close,但是您的新线程已经获得了要执行的可运行对象。您确实想给它一个包装器,它运行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();
}

如果我没有指出isFinished的线程不安全性,那我就是失职了。在不添加同步的情况下,不能保证您注意到线程何时完成。我建议你加上:

public synchronized boolean getIsFinished()
{
    return isFinished;
}

public synchronized void setIsFinished(boolean finished)
{
    isFinished = finished;
}
并使用这些方法获取或设置isFinished标志。
鉴于您在此处缺乏同步,您可能会看到其他线程安全异常,这取决于您的r.run()方法和您的其他“观察者”是否也在不同步的情况下共享数据。

关闭,但您的新线程已经获得了要执行的可运行对象。您确实想给它一个包装器,它运行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();
}

如果我没有指出isFinished的线程不安全性,那我就是失职了。在不添加同步的情况下,不能保证您注意到线程何时完成。我建议你加上:

public synchronized boolean getIsFinished()
{
    return isFinished;
}

public synchronized void setIsFinished(boolean finished)
{
    isFinished = finished;
}
并使用这些方法获取或设置isFinished标志。
鉴于您在此处缺乏同步,您可能会看到其他线程安全异常,这取决于您的r.run()方法和其他“观察者”是否也在共享数据而不同步。

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

这有点奇怪,因为您都将Runnable传递给构造函数,但使用方法声明中的引用(而不是线程中的引用)调用它。但它“起作用”,只是有一个冗余


顺便说一句,不要忘记在匿名类中重写:)

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

这有点奇怪,因为您都将Runnable传递给构造函数,但使用方法声明中的引用(而不是线程中的引用)调用它。但它“起作用”,只是有一个冗余


顺便说一句,不要忘记在匿名类中使用
@Override

您几乎不应该将
可运行的
传递到
线程的构造函数中,并重写线程的
run()
方法

以下两段代码基本相同:

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

new Thread( r ).start( );
这里有另一种方法可以通过重写
run()
来完成同样的事情:


您几乎不应该将
Runnable
传递到
线程的构造函数中,并重写线程的
run()
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( );
  }
}
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();
    }
}