首页 > 解决方案 > 可以暂停/恢复并执行用户的可运行操作的通用线程

问题描述

目的:为了创建一个独立于调用它的父类的通用线程类, 可以由调用它的父类启动/停止/暂停/恢复并执行用户定义的任务(通过可运行)

我的研究SO_1 SO_2 SO_3 SO_4 SO_5 SomeBlog SomeBlog2 OracleBlog

问题:据我了解:

因此,基于此,我编写了一个通用 Thread 类,它应该通过 ui 按钮运行用户的一组任务和播放/暂停/恢复/停止,但它不起作用

通用线程.java


public class PausibleThread extends Thread {

    public static final String TAG ="PausibleThread>>";

    @Nullable
    PausibleRunnable runnable ;

    public PausibleThread(@Nullable Runnable target) {
        super(target);
        PausibleRunnable r = new PausibleRunnable(target);
        runnable=r;
    }

    @Override
    public synchronized void start() { super.start(); }

    public  synchronized void stopThread(){ this.interrupt(); }
    public  synchronized  void pauseThread(){ runnable.pause(); }
    public  synchronized  void resumeThread(){ runnable.resume(); }

PausibleRunnable.java:


import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class PausibleRunnable implements Runnable {
    private Object lockerObject;
    private boolean isPaused;
    private boolean isFinished;
    public static final String TAG="PausibleRunnable";

    @Nullable
    Runnable usrAction = null;

    public PausibleRunnable(@NonNull Runnable usrAction) {
        lockerObject = new Object();
        isPaused = false;isFinished = false;

        this.usrAction = usrAction;
    }

    public void run() {
        while (!isFinished) {

            if(isPaused) {
                runPauseLoop();
            }
            else {
                runUserAction();
                isFinished=true;
            }

        }
    }

    private void runPauseLoop() {
        synchronized (lockerObject) {
            while (isPaused) {

                try { lockerObject.wait(); }
                catch (InterruptedException e) { e.printStackTrace(); }

            }
        }
    }

    private void runUserAction() {
        if(usrAction !=null){ usrAction.run(); }
        else { Log.e(TAG, "run: userAction is NULL" ); }

    }




    public void pause() {
        synchronized (lockerObject) { isPaused = true; }
    }

    public void resume() {
        synchronized (lockerObject) {
            isPaused = false;
            lockerObject.notifyAll();
        }
    }


}

Ui 创建一个 Pausible Thread 并实现它的各种功能:

    //full class implementation at : https://paste.ubuntu.com/p/cTpW5Wt3Fy/
    int totalRunTime = 20 * 5;
    Pausible thread bgThread;

    private void initThread() {
        Runnable r = () -> {
            try {
                while (totalRunTime > 0) {
                    Thread.sleep(500);
                    totalRunTime--;
                    updateUi();

                }
            }
            catch (Exception e) { e.printStackTrace(); }
        };
        bgThread = new PausibleThread(r);

    }

    private void updateUi() {
        String data = "TotalRunTime=" + totalRunTime;
        runOnUiThread(() -> tvTerminal.setText(data));
    }

    @Override
    public void onClick(View v) {
        if (bgThread == null) {
            makeShortToast("Can't perform action, bg thread is null");
            return;
        }

        if (v.getId() == fabPause.getId()) {bgThread.pauseThread(); }
        else if (v.getId() == fabResume.getId()) { bgThread.resumeThread(); }
        else if (v.getId() == fabStop.getId()) { bgThread.stopThread(); }
        else if (v.getId() == fabStart.getId()) { bgThread.start(); }

    }

但这不起作用。为什么?我在这里进行了疯狂的猜测,但我认为 runnable 只是运行用户的操作来运行一个大循环,而不是反复检查播放/暂停。那我该怎么办?

用户界面示例图片:https ://i.imgur.com/kmj3Bwt.png

标签: javaandroidmultithreading

解决方案


你问:“但这不起作用。为什么?”

我回答:你的解决方案不起作用,因为你总是在循环里面运行runUserAction。你永远不会跳出那个循环来检查你是否被暂停。

恐怕你必须改造你的解决方案以在更短的循环中运行 usrAction,否则你将失去状态(假设你从外部中断该循环),这将导致未定义的行为,或者你只会打破当它结束时,或者你会在你不想暂停的状态下暂停你的循环[例如,在进行网络调用时——恢复后你会得到一个 SocketTimeoutException]。

我建议您使用前一种方法,因为它更优雅。

编辑:

另一种可能的解决方案:usrAction 中的每次迭代都检查PausableThread 的状态,即查看它是否暂停、停止或其他。

尝试这个:

PausableRunnable.java

    public synchronized boolean canContinue() throws Exception {
        synchronized (lockerObject) {
            if (isPaused) {
                lockerObject.wait();
            }
            if (isFinished) {
                return false;
            }
            return true;
        }
    }

PausableThread.java

    public boolean canContinue() throws Exception {
        return runnable.canContinue();
    }

和 Application.java

private void initThread() {
        Runnable r = () -> {
            try {
                while (totalRunTime > 0) {
                    if (bgThread.canContinue()) { // <--- !!!!!!
                        Thread.sleep(200);
                        totalRunTime--;
                        updateUi();
                    }
                }
            }
            catch (Exception e) { e.printStackTrace(); }
        };
        bgThread = new PausibleThread(r);

    }

这样,您可以运行您的应用程序 Runnable,并且在可运行对象可以承受的时间仍然遵守 PausableThread 的状态。即在交易或其他不应中断的计算之前/之后。

编辑2:

随意在暂停或恢复等方法上丢失“同步”修饰符,因为您已经在其中的同步块中进行操作。


推荐阅读