-1

I'm a student in Denmark trying to make a school project. What I'm working on at this moment is a reader class that takes in a string then prints out word by word and/or letter by letter.

I did some research and found out that Thread.sleep(time) did exactly what I needed it to do. But after I used it I found out it does not work properly! I tried to research some more and found something called a ThreadPoolExecutor but I can figure out how it works in my case.

My reader:

public class TextReader {

    // Print method to print word by word from a string
    public void wordByWord(String text) throws InterruptedException {
        String[] words = text.split(" ");
        for (int i = 0; i < words.length; i++) {
            System.out.print(words[i] + " ");
            Thread.sleep(250);
        }
    }

    // Print method to print letter by letter from a string
    public void letterByLetter(String text) throws InterruptedException {
        String[] words = text.split(" ");
        for (int i = 0; i < words.length; i++) {
            String word = words[i] + " ";
            char[] letters = (word.toCharArray());
            for (int j = 0; j < letters.length; j++) {
                System.out.print(letters[j]);
                Thread.sleep(250); //so it does not print all the letters at once
            }
        }
    }
}

The reason why Thread.sleep(time) not works in my case is because I need to print to the console and by using Thread.sleep(time) it does not print like a waterfall. It prints either the string I'm trying to break down (time lower than 250ms) or a few letters a once (250 ms) or is just so slow I can't look at it... (over 250ms). I need it to run fast and smooth! So it looks like someone is writing it.

Humeroush
  • 1
  • 4

4 Answers4

3

I think I successfully recreated your problem. Every delay lower than about 205 ms seem to cause updating problems. Sometimes the words/letters don't appear but then at the next interval multiple words/letters appear at the same time.

This seems to be a limitation of the Console I/O performance (See this answer). There isn't really anything you can do about this. If you want to output text with a short, minimal delay like this, you need to program your own GUI (for example JavaFX). This will probably solve the performance issues.

Outputs at different delays

205 ms

Desired output, but too slow (205ms)

190 ms

Undesired, "unsmooth" output (190ms)

Impulse The Fox
  • 2,638
  • 2
  • 27
  • 52
2

Thread's sleep method takes milliseconds to stop the execution of current thread for specified milliseconds. If it's slow, you can pass less MS and if it's fast then you can increase the timings. So you can tweak according to your need.

ExecutorFramework is a different thing. It a way to submit your runnable task to the threads managed by ExecutorFramework.

Uzair
  • 469
  • 3
  • 13
1

What you are doing is putting a Thread to sleep for that time. That means the thread will become unblocked after that time, however you aren't accounting for the overhead of context switching from another thread. What you want is something more like this

likeabbas
  • 115
  • 1
  • 10
  • I think you are on the right track. But how would it look in my code? Still new to Java, and I can't figure it out with your link. – Humeroush Apr 18 '18 at 19:11
  • If you are new to java, I do not recommend using anything with Thread. All you'd need to do is import java.util.concurrent.TimeUnit:, and use the TimeUnit.MILLISECONDS.sleep(250) – likeabbas Apr 18 '18 at 19:13
  • I tested `TimeUnit.MILLISECONDS.sleep(190);` and the result is the same, bad, "unsmooth" output. – Impulse The Fox Apr 18 '18 at 19:16
0

Tried out the ScheduledExecutorService approach and seems to work fine. There's some optimization to be done and some hoops to jump through to wait for the scheduled printing to finish, but it doesn't seem to display the lag (in the two consoles I tried - Eclipse output and Windows Bash).

public class Output {
    public static void main(String[] args) {
        String toPrint = "Hello, my name is Voldemort, but few call me that.";
        StringPrinter printer = new StringPrinter();
        printer.print(toPrint, Output::byCharacter, 30);
        System.out.println();
        printer.print(toPrint, Output::byWord, 150);
    }

    private static List<String> byWord(String toSplit) {
        Iterable<String> it = () -> new Scanner(toSplit);
        return StreamSupport.stream(it.spliterator(), false).map(s -> s + " ").collect(Collectors.toList());
    }

    private static List<String> byCharacter(String toSplit) {
        return toSplit.chars().mapToObj(i -> "" + (char) i).collect(Collectors.toList());
    }
}

class StringPrinter implements Runnable {
    // using an array to be most efficient
    private String[] output;
    private int currentIndex;

    // the service providing the milliseconds delay
    private ScheduledExecutorService printExecutor;

    public void print(String toOutput, Function<String, List<String>> split, int delay) {
        if (printExecutor != null) {
            throw new IllegalStateException();
        }
        printExecutor = Executors.newSingleThreadScheduledExecutor();
        List<String> list = split.apply(toOutput);
        output = list.toArray(new String[list.size()]);
        currentIndex = 0;

        printExecutor.scheduleWithFixedDelay(this, 0, delay, TimeUnit.MILLISECONDS);

        // wait until output has finished
        synchronized (this) {
            while (printExecutor != null)
                try {
                    wait(); // wait for printing to be finished
                } catch (InterruptedException e) {}
        }
    }

    @Override
    public void run() {
        if (currentIndex < output.length) {
            System.out.print(output[currentIndex++]);
        } else {
            // mark this print run as finished
            printExecutor.shutdown();
            printExecutor = null;
            synchronized (this) { notifyAll(); }
        }
    }
}
daniu
  • 14,137
  • 4
  • 32
  • 53