0

我正在尝试使用线程在 java 中的 CLI 上显示进度条,同时进行长时间操作(为一批大文件生成 md5sum)。

我写了一些代码,它可以工作,但我想知道我是否正确使用线程,因为我对此很陌生。

我有两个类文件,ProgressThreadTest.java并且CountToABillion.java

CountToABillion.java:

public class CountToABillion implements Runnable {

    double count = 0;

    public void echoCount() {
        System.out.println(count);
    }

    @Override
    public void run() {     
        for (double x=0;x<1000000000;x++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            count = x;
            echoCount();
        }       
    }   
}

ProgressThreadTest.java:

public class ProgressThreadTest {

    public static void main(String[] args) throws InterruptedException {

        Thread doCount=new Thread(new CountToABillion());
        doCount.start();
    }   
}

它按预期工作,并在命令行上向上计数。

有人对这是否是做线程的好方法有任何意见吗?

另外,因为我正在更新计数循环中的进度,它会每 10 毫秒更新一次。如何将代码更改为每秒仅输出一次计数?

4

4 回答 4

2

使用javax.swing.Timer可能是更简单的解决方案:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;

public class CountToABillion implements Runnable {

    double count = 0;
    Timer progressTimer;

    public void echoCount() {
        System.out.println(count);
    }

    @Override
    public void run() {
        progressTimer = new Timer(1000, new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                echoCount();
            }
        });
        progressTimer.setRepeats(true);
        progressTimer.start();

        for (double x=0;x<1000000000;x++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            count = x;
        }
        progressTimer.stop();
    }
}

使用java.util.concurrent.ScheduledThreadPoolExecutor是更好且更具可扩展性的解决方案:

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CountToABillion implements Runnable {

    double count = 0;
    ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);

    public void echoCount() {
        System.out.println(count);
    }

    @Override
    public void run() {
        Runnable task = new Runnable() {
            public void run() {
                echoCount();
            }
        };
        exec.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);

        for (double x=0;x<1000000000;x++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            count = x;
        }
        exec.shutdownNow();
    }
}
于 2013-07-05T12:18:51.903 回答
1
@Override
public void run() {     
    for (int i = 0; i < 1000000000; i++) {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count = i;

        if(i % 100 == 0) {
            echoCount();
        }
    }
}

通过使用i % 100 == 0,您正在检查是否i可以被 100 整除而没有剩余值。如果是这种情况,则意味着您已经运行了 100 次 10 毫秒,即 1000 毫秒,即 1 秒。所以你会输出你的echoCountt()每一秒。

这是使用线程的好方法。但是您的线程将继续运行,直到它计数到 10000000 秒。

在旁注中,您应该在 for 循环中使用“i”而不是“x”。像这样使用更广泛,对于有经验的 Java 开发人员来说更容易阅读

于 2013-07-05T12:08:34.763 回答
1

选项1:

    int sleep = 10; //ms
    int echo = 1000; //ms
    for (double x=0;x<1000000000;x++) {
        try {
            Thread.sleep(sleep);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        count = x;
        if((x*sleep % echo) == 0) {
            echoCount();
        }
    }

选项 2:

  • 创建一个新的类来管理你的计数器,它应该可以添加、重置等。您必须确保它是线程安全的(写入新值以防您想从各种线程进行更新)。
  • 创建一个线程,在给定的时间间隔内增加计数器
  • 创建另一个线程,以其他给定的时间间隔轮询当前计数并打印。
于 2013-07-05T12:14:06.133 回答
1
long last=0;
@Override
public void run() {     
    for (double x=0;x<1000000000;x++) {
        try {
            doWork();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        count = x;
        if((System.currentTimeMillis()-last)>=1000) //post every second
        {
            last=System.currentTimeMillis();
            echoCount();
        }
    }       
} 

假设“工作”不超过一秒,这将每秒打印一次计数。

于 2013-07-05T12:15:25.763 回答