javamultithreadingsynchronize

Java Multithreading two classes in main


I am very new to programming, and I am trying to write a Java program with the Timer and ChecksUserInput classes shown below. How do I get them to run at the same time in the main class?

I am also having issues with printing out the word length in ChecksUserInput.

main.java:

package application;

public class Main {
    public static void main(String[] args) {
        CreateBoard board = new CreateBoard();
        board.run();

        Timer timer = new Timer();
        timer.run();

        ChecksUserInput input = new ChecksUserInput();
        input.run();
    }
}

timer.java:

package application;

public class Timer {
    private static void time() {
        final int mili = 1000;
        final int sec = 60;
        final int oneMinute = (mili * sec);

        System.out.println("Start 3 minute timer");
        sleep(oneMinute * 2);

        System.out.println("One minute remaining...");
        sleep(oneMinute);

        System.out.println("Time's up!");
    }

    private static void sleep(int sleepTime) {
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        time();
    }
}

checksuserinput.java:

package application;

import java.util.*;

public class ChecksUserInput {
    private static String UserInput() {
        Scanner sc = new Scanner(System.in);
        System.out.println("Begin entering words!");

        String word = null;
        for (int i = 0; i < 10000; i++) {
            word = sc.nextLine();
        }

        return word;
    }

    private static int length(String word) {
        int wordLength = word.length();
        return wordLength;
    }

    public void run() {
        String userWord = UserInput();
        int wordLength = length(userWord);
        System.out.println(wordLength);
    }
}

Solution

  • The foundation of multi-threading in Java is the Thread class. The general structure for usage is:

    Thread newProcess = new Thread(processToRun); //Create a thread which will execute the process
    newProcess.setDaemon(true/false); //when false, the thread will keep the JVM alive beyond completion of 'main'
    newProcess.start(); //Start processToRun in a new thread
    

    To start several independent processes, this should be sufficient. For example, the following starts 10 threads each of which will print the index in the loop. At the end, the process sleeps for 5 milliseconds because the spawned threads are daemon. Removing this may cause the process to terminate before any messages are printed.

        public static void main(String args[]) throws Exception
        {
            for(int i = 0; i < 10; i++) { int index = i; start(() -> System.out.println(index)); }
            Thread.sleep(5);
        }
    
        public static void start(Runnable processToRun)
        {
            Thread newProcess = new Thread(processToRun);
            newProcess.setDaemon(true);
            newProcess.start();
        }
    

    Beyond this point questions start to get more complicated/contextual. Ex:

    1. How can processes running in 2 threads communicate with each other?
    2. How can processes running in 2 threads access/modify common state between them?

    In the context of creating a simple game, one option is to use Queues to feed user inputs to the game and have the game process updates in a single thread. The following sample listens for the user inputting commands (Up, Down, Left, Right) on the main thread and adds valid commands to a queue. Valid commands are polled and processed in a different thread to update the location on the board.

    Sample:

        public static void main(String args[])
        {
            Board board = new Board();
            BlockingQueue<Move> movesQueue = new ArrayBlockingQueue<>(100);
            Scanner systemListener = new Scanner(System.in);
            start(() -> routeBoardMovesToQueue(board, movesQueue)); /*route moves from the queue to the board in a new thread*/
            while(true)
            {
                Optional<Move> nextMove = Move.resolve(systemListener.nextLine());
                if(nextMove.isPresent())
                    movesQueue.offer(nextMove.get()); /*Write moves from System.in to the queue*/
                else
                    System.out.println("Invalid Move Provided");
            }
        }
        
        public static void routeBoardMovesToQueue(Board board, BlockingQueue<Move> movesQueue)
        {
            try
            {
                while(true)
                {
                    Move next = movesQueue.poll(100_000, TimeUnit.DAYS);
                    if(next != null) board.performMove(next);
                }
            }
            catch(InterruptedException ignored){ System.out.println("Stopping"); }
        }
    
        public static void start(Runnable processToRun)
        {
            Thread newProcess = new Thread(processToRun);
            newProcess.setDaemon(true);
            newProcess.start();
        }
    
        public static final class Board
        {
            private final Location location;
            public Board(){ this.location = new Location(); }
            public void performMove(Move move)
            {
                switch(move)
                {
                    case Up:    location.y += 1; break;
                    case Down:  location.y -= 1; break;
                    case Right: location.x += 1; break;
                    case Left:  location.x -= 1; break;
                }
                System.out.println("New Position: (" + location.x + ", " + location.y + ")");
            }
    
            public static class Location{ int x = 0; int y = 0; }
        }
    
        public enum Move
        {
            Up, Down, Left, Right;
            public static Optional<Move> resolve(String move){ return Stream.of(Move.values()).filter(mv -> Objects.equals(move, mv.name())).findAny(); }
        }