【问题标题】:How can thread already be started?线程如何已经启动?
【发布时间】:2014-04-17 16:14:49
【问题描述】:

我有一个单实例线程类。

public class LogThread extends Thread{

    private static LogThread instance = null;
    private volatile boolean isRunning = false;
    private final static Object instanceLock = new Object();

    public static synchronized LogThread getInstance(){
        synchronized(instanceLock){
            if(instance == null)
                instance = new LogThread();
        }
        return instance;
    }

    @Override
    public run(){
        //Doing some run stuff
        //Once run is finished

        synchronized(instanceLock){
            isRunning = false;
            instance = null;
        }
    }

    @Override
    public synchronized void start() {
        synchronized (instanceLock){
            if(!isRunning){
                isRunning = true;
                super.start();
            }
        }
    }
}

每次获取实例时,我都会从另一个线程调用 start,并且每隔一段时间,我会在 com.......LogThread.start 中收到 IllegalThreadStateException,第 x 行线程已经启动。

如果我在启动线程之前设置了 isRunning 并基于 instanceLock 同步它,如何启动线程。

编辑:: 我已将我的 getInstance() 编辑到以下:

 public static synchronized LogThread getInstance(){
     synchronized(instanceLock){
         if(instance == null){
             instance = new LogThread();
             instance.start();
         }
     }
 }

它应该停止任何尝试启动已经启动的线程。

【问题讨论】:

  • 在您的情况下实际上应该是不可能的,您是否曾经将isRunning 设置回false?例如。在run()
  • 是的,我将 isRunning 设置为 false,就在我在同步线程中将实例设置为 null 之前,将发布更新的代码

标签: java android multithreading thread-safety


【解决方案1】:

顺序是这样的:

  • 线程 A 调用 getInstance。线程 A 锁定了一段时间,是时候检查instance 是否不为空,此时它不是。
  • 线程 LogThread 完成执行,isRunning 设置为 false。 instance 设置为 false,但仍被线程 A 保留
  • 线程 A 在实例上调用 startisRunning 为 false,因此会调用 start,从而导致崩溃。

一个快速的解决方法是不要将isRunning 设置为false,因为没有任何充分的理由让该实例可以重新启动。然后应将其重命名为 isStarted 以与其所做的保持一致。

按照@Mani 的建议,一个合适的解决方案是使用newSingleThreadExecutor

【讨论】:

  • 感谢您的信息。我决定采用稍微不同的实现,在 getInstance() 中启动线程
【解决方案2】:
It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.

来自Thread.start

您不应该启动已经启动的线程。即使线程完成了它的工作(运行完成)。

它只是检查threadStatus 并抛出异常

  if (threadStatus != 0)
            throw new IllegalThreadStateException();

如果我理解清楚,您希望确保只有一个线程用于此任务,并且您希望重用同一个线程。 恕我直言,你有两个选择,

选项 1:每次都创建新线程。确保前一个线程完成了它的工作。它很昂贵,因为每次创建新线程都很昂贵

选项 2:Executors.newSingleThreadExecutor() 使用它。这将确保只创建一个线程。

一般来说,避免扩展线程,使用Runnable

如果你只想使用一个线程。并且有很多工作。您可以使用以下内容。

class LogJob implements Runnable{
    @Override
    public void run() {
        // Do your Job
    }
}

  ExecutorService singleThread = Executors.newSingleThreadExecutor();
LogJob job = new LogJob();
for (int i=0;i<100;i++){
    singleThread.submit(job);
}
singleThread.shutdown();

// 对于演示,我已将其置于循环中。你可以打电话给singleThread.submit(job);。在任何给定时间最多只能运行一个作业。你所有的工作都将完成。

您不需要任何同步块/方法

【讨论】:

  • start 中的代码检查它是否已经启动(isRunning 变量选择错误,应该是 isStarted)。所以如果它还没有开始,它应该调用 super.start() 否则什么都不做
  • 无论变量名称如何,当您将变量设置回 false 时,super.start 将再次调用,它将尝试启动已经完成的线程。这是错误的
  • 我之前更新了这个问题,我唯一一次将 isRunning 切换为 false,就在我将实例设置为 null 之前,这是在同步块中,
  • 即使你设置了 instance == null ,那些已经持有 instance 并在 start syncronized block 中等待的线程呢?一旦当前线程退出运行,它将释放锁,其他等待的线程将启动相同的线程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-21
  • 2011-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-21
  • 2016-11-01
相关资源
最近更新 更多