【问题标题】:Thread won't die and two of them keep overlapping线程不会死,其中两个一直重叠
【发布时间】:2020-02-03 11:31:49
【问题描述】:

我需要一个一次只运行一次的线程,例如,如果它第一次被调用,它将运行,如果它被第二次调用,第一次应该完全停止并被允许死掉,然后一个新的应该取而代之。 我进行了一个小测试,看看每次执行之间实际发生了什么,结果表明线程没有死,而是两个线程同时执行:

public class Test {
    Worker worker = new Worker();

    @Override
    public void valid() {

        try {
            if (worker.running) {
                worker.running = false;
                worker.join();
            }

        } catch (InterruptedException iex) {
            worker.running = false;
        }

        worker = new Worker();

        worker.start();

    }

    private final class Worker extends Thread {
        private volatile boolean running = true;

        @Override
        public void run() {
            while (running) {
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException iex) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
}

结果如下:

//Upon first execution
Thread-4
Thread-4
Thread-4
Thread-4

//When I execute it again
Thread-7
Thread-4
Thread-7
Thread-4
Thread-7
Thread-4

我尝试使用ExecutorServicewhile(!Thread.currentThread.isInterrupted) 代替布尔标志,并得到相同的结果。 如何正确停止“Thread-4”并只运行其中一个?

实际问题来自一个线程,该线程将循环遍历列表并根据请求更新不和谐聊天中的内容,线程所做的是根据肾脏的建议收听输入和更改我正在尝试使用 executor.submit()和未来

private ExecutorService executor = Executors.newSingleThreadExecutor();

    private Future<Void> worker;

    private void setupImageThread() {

        if (!worker.isDone() && !worker.isCancelled()) {
            worker.cancel(true);
        }

        this.worker = (Future<Void>)executor.submit(new Cycler(Listener.queue(), this.links, Cel.cMember()));

        ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);

        Runnable timeout = () -> {

            executor.shutdown();

        };

        ses.schedule(timeout, 100, TimeUnit.SECONDS);
    }

如何在 Future 第一次创建时对其进行初始化?

【问题讨论】:

  • Test 对象是如何创建的? Test.valid 是怎么调用的?
  • 它由来自EventListener的用户输入调用,当它被调用时它只运行一次valid(),该类实际上实现了一个带有该方法的功能接口。如果重要的话,这来自 Discord JDA 项目。
  • 我们保证我们创建一个测试实例?然后在同一个实例上多次调用 valid() ? (我对这个问题没有理论,我只是想掌握全局)
  • 它的创建方式是对类的引用以 TEST(Test::new) 的形式存储在 Enum 常量中,每次调用它都会使用 @ 调用指定类中的有效方法987654327@ 所以我猜每次都是一个新实例?我对多线程还很陌生,所以我不确定。
  • 我建议使用single thread executor,它可以保证任务的顺序处理。

标签: java multithreading


【解决方案1】:

使用single thread executor service,我会尝试这样的事情:

public class Test {

    private static ExecutorService executor = Executors.newSingleThreadExecutor();

    Future<Void> worker;

    public Test() {
        this.worker = executor.submit(new Worker());
    }

    @Override
    public void valid() {
        if (!worker.isDone() && !worker.isCancelled()) {
            worker.cancel(true);  // Depends on whether you want to interrupt or not
        }

        this.worker = executor.submit(new Worker());
    }
}

并使Worker实现Runnable

【讨论】:

  • Future&lt;Worker&gt; worker 在检查第一次执行是否完成时不会为空吗?它在该行抛出 NullPointerException。
  • 是的,当然,我把初始化留给你,我不知道确切的条件。例如,您可以在构造函数中提交第一个工人。
  • 对不起,我不确定如何初始化它。而且我还收到从 Future> 到 Future 的类型不匹配错误,仅向 (Future) 添加强制转换是否正确?
  • 对不起,类型不正确,对于RunnableFuture的类型应该是Void。您可能需要投射它,但投射应该没问题。我更新了答案...
  • 我用关于调用的实际类的更多信息更新了问题,它的运行方式与测试类相同,在它之前有一个方法用图像 URL 填充列表,然后是那个方法调用来设置线程,Cycler类实际上有参数,所以在构造函数中启动它不会正常工作
【解决方案2】:

看来valid方法可以同时调用多次。这意味着,这些调用中的每一个都将等待一个线程(Worker)结束,而它们中的每一个都会创建自己的Worker,并且您会丢失指向它的指针,因此无法阻止一堆新创建的工作者。 您应该使 valid 方法同步:synchronized void valid() 它将阻止创建许多工作人员:

@Override
synchronized public void valid() {
    ...
}

还有一点要说。您将while循环放在try-catch之外,这是错误的:如果tread被中断,中断不会杀死它,因为下一个interation开始了,所以应该是这样的:

@Override
public void run() {
    try {
        while (running) {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(2000);
        }
    catch (InterruptedException iex) {
       //you don't need here Thread.currentThread().interrupt() call, because the thread has alredy been interrupted. 
       // The return statement here is also obsolete, I just use it as an example, but you can use empty braces.
       return; 
    }
    }
}

【讨论】:

  • 恕我直言,如果您使 valid 同步,它只会阻止其并发调用。但由于它为每次调用创建一个新线程,因此可以通过这种方式创建新的工作线程......
  • 是的,但总是只有一个工作线程。作者的问题是他的代码创建了一堆线程并且他丢失了指向它们的指针,所以不可能发现它们,我们遇到了主题开始描述的情况。
  • 我想我不明白。假设创建了类,因此启动并运行了一个工作线程。一秒钟后,valid 被调用。将创建一个新的工作线程,原来的工作线程标记为停止,但它将继续运行一秒钟。这是我对这个问题的理解。但是当然,如​​果valid被同时调用,它会创建不可停止的工作线程。
  • 不,在当前 Worker 停止之前不会创建新线程 Worker,因为调用了 worker.join()
猜你喜欢
  • 1970-01-01
  • 2022-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-20
  • 2014-04-16
  • 1970-01-01
相关资源
最近更新 更多