如何在Java中创建线程控制台进度条?

时间:2018-03-07 06:05:35

标签: java

我正在尝试用Java创建一个工作进度条程序,因此它可以支持控制台和GUI应用程序。

这个想法是使用线程来提供当前进度信息,但似乎线程代码运行不正常。并发对我来说是如此新鲜。

我希望它在每次加强时都会前进,而不是在进度条赶上之前提前完成循环。我猜问题是时间问题?

[=====>                                            ] 10%  1
2
3
4
5
6
7
8
9
10

[==================================================] 100%  

有人能告诉我出了什么事吗?

主要代码

package console;
import java.util.ArrayList;
import console.ProgressThread;

public class ConsoleProgressBar
{
    private static final long REFRESH_DELAY = 50;
    private ProgressValue progress;
    private ProgressThread target;
    private Thread thread;

    protected static class ProgressValue
    {
        protected long total = 0;
        protected long current = 0;

        protected ProgressValue(long n)
        {
            total = n;
        }

        protected synchronized void setMaxTotal(long n)
        {
            total = n;
        }

        protected synchronized void stepBy(long n)
        {
            current = current + n;

            if (current > total) total = current;
        }

        protected synchronized void stepTo(long n)
        {
            current = n;

            if (current > total) total = current;
        }

        protected synchronized long getCurrent()
        {
            return current;
        }

        protected synchronized long getTotal()
        {
            return total;
        }
    }

    public ConsoleProgressBar(long totalItem)
    {
        this(totalItem, REFRESH_DELAY);
    }

    public ConsoleProgressBar(long totalItem, long refreshDelay)
    {
        progress = new ProgressValue(totalItem);
        target = new ProgressThread(progress, refreshDelay);
    }

    public void start()
    {
        thread = new Thread(target);
        thread.start();
    }

    public void stepBy(long n)
    {
        progress.stepBy(n);
    }

    public void stepTo(long n)
    {
        progress.stepTo(n);
    }

    public void step()
    {
        progress.stepBy(1);
    }

    public void setMaxTotal(long n)
    {
        progress.setMaxTotal(n);
    }

    public void stop()
    {
        target.terminate();

        try
        {
            thread.join();
        }

        catch (InterruptedException ex)
        {
        }
    }

    public long getCurrent()
    {
        return progress.getCurrent();
    }

    public long getTotal()
    {
        return progress.getTotal();
    }

    public static void main(String[] args)
    {
        ArrayList<Integer> test = new ArrayList<>();
        ConsoleProgressBar bar = new ConsoleProgressBar(10, 50);

        bar.start();

        for (int i = 0; i < 10; i++)
        {
            int sum = i + 5;

            test.add(sum);

            bar.step();

            System.out.format("%s%n", bar.getCurrent());
        }

        bar.stop();
    }
}

线程代码

package console;

import console.ConsoleProgressBar.ProgressValue;

public class ProgressThread implements Runnable
{
    private static final int WIDTH = 50;
    private volatile boolean terminated;
    private ProgressValue progressRef;
    private long timeMS;

    public ProgressThread(ProgressValue ref, long refreshDelay)
    {
        progressRef = ref;
        timeMS = refreshDelay;
        terminated = false;
    }

    private void refreshProgressBar()
    {
        StringBuilder sb = new StringBuilder("\r[");
        int percent = (int) Math.floor(100.0 * progressRef.current / progressRef.total);

        for (int i = 0; i < WIDTH; i++)
        {
            if (i < (percent / 2)) sb.append("=");
            else if (i == (percent / 2)) sb.append(">");
            else sb.append(" ");
        }

        sb.append("] %s  ");

        if (percent >= 100) sb.append("%n");

        System.out.printf(sb.toString(), percent + "%");
    }

    void terminate()
    {
        terminated = true;
    }

    public void run()
    {
        try
        {
            while (terminated == false)
            {
                refreshProgressBar();
                Thread.sleep(timeMS);
            }

            refreshProgressBar();
        }

        catch (InterruptedException exc)
        {
        }
    }
}

1 个答案:

答案 0 :(得分:1)

为什么只需要一个你想要实现的任务就需要一个多线程应用程序?

尽管如此,为了达到你想要的目的,我建议你将执行完全转移到线程类或主类中。

如果主应用程序要运行其他东西,那么理想情况下你会将执行放在线程类中。但是在这里我把执行放到了主类中。它也可以轻松进入线程类。

例如,我在run()中编辑了ProgressThread就是这样,

public void run()
{
    while( terminated )
    {
    }
}

我在main中对此ConsoleProgressBar进行了编辑,

public static void main(String[] args)
{
    ArrayList<Integer> test = new ArrayList<>();
    ConsoleProgressBar bar = new ConsoleProgressBar(10, 50);

    bar.start();

    for (int i = 0; i <= 10; i++)
    {
        int sum = i + 5;

        test.add(sum);

        bar.refreshProgressBar();
        System.out.format( "%s", bar.getCurrent() );
        bar.step();
        bar.sleep( 1000 );
    }

    bar.stop();
}

请注意,我已将方法sleep( int n )refreshProgressBar()添加到栏中,因此我可以调用线程方法,类似于您对bar.start()bar.stop()所做的操作。

为了清楚起见,我在ProgressThread中仅仅为了示例而将refreshProgressBar更改为public并添加了以下内容,

void sleep( int n )
{
    try
    {
        Thread.sleep( n );
    }
    catch( InterruptedException ie )
    {
        ie.printStackTrace();
    }
}

以及以下ConsoleProgressBar

private void sleep( int n )
{
    target.sleep( n );
}

private void refreshProgressBar()
{
    target.refreshProgressBar();

}

输出(每行以一秒间隔打印)是

[>                                                 ] 0%  0
[=====>                                            ] 10%  1
[==========>                                       ] 20%  2
[===============>                                  ] 30%  3
[====================>                             ] 40%  4
[=========================>                        ] 50%  5
[==============================>                   ] 60%  6
[===================================>              ] 70%  7
[========================================>         ] 80%  8
[=============================================>    ] 90%  9
[==================================================] 100%  10

不确定这是否是您要找的,但我建议将执行放在一个地方。