3

I am fork a new thread on my service's @Postconstruct method, and in the new thread, a infinite loop is running.

My test is just invoke the service using spring mvc test:

ResultActions result = this.mockMvc.perform(post("/test").with(httpBasic(user, pwd)).contentType("application/json").content(test))
        .andDo(MockMvcResultHandlers.print())
        .andExpect(status().isOk());

And the test just hangs there, waiting for the infinite loop thread to stop. but when the service is started normally, the test is fine. Any idea why? And how to fix it.

here is the code in my service java:

@Postconstruct
private void init() {
   invoke();
}

private void invoke() {

 Runnable task = () -> {
     while(true) { ... }
  }
 Thread t;
 for(int i=0; i<3; i++) {
     t = new Thread(task);
     t.setName("test-" + i);
     t.start();
  }
 }
Cherry Zhang
  • 101
  • 1
  • 11
  • the real question is how you are invoking the thread. That is likely the issue. – Bojan Petkovic Mar 30 '17 at 03:40
  • I've added the code how I invoke the thread, @Bojan Petkovic, could you help to see if I've done anything wrong? – Cherry Zhang Mar 30 '17 at 04:07
  • @GhostCat, I advoid the problem by doing something like your suggestion, but I still don't know why it happens, for before I refactoring my code, the testcase did work smoothly, I just want to wait to see if anyone else can point the root casue out. I can accept your answer now since no more answer is provided for a long time. – Cherry Zhang Apr 12 '17 at 06:22

2 Answers2

1

Suggestion: step back from using "bare metal" Threads. There are nice abstraction concepts, like ExecutorService and things like Futures and Promises.

The point is: one can use dependency injection to provide such an ExecutorService to the production code; and then you can define your own service ... that does everything on the same thread.

Meaning: avoid unit tests that deal with multiple threads - as that often leads to additional waiting, or "flakiness" as you never now exactly how long your test will be running.

Community
  • 1
  • 1
GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    Sometimes you use 3rd part libs which use multithreading without you knowig it. This answer isn't answering this question. you provide workaround. – Peter Mar 08 '21 at 13:21
  • @Peter When you have no knowledge or control over 3rd party libraries doing their own threading business, what makes you think there can be a universal answer to that anyway? As I commented back then on the question: nothing prevents other people from giving a *better* answer. I would quickly upvote that, too. – GhostCat Mar 08 '21 at 15:17
0

Take a look at this example (with a debugger)

Notice that the main will not be exited until all the threads are done. You can easily do this with putting a breakpoint on a while (run) and change the value of run slowly. Once you turn off all 3 threads, then your main thread will exit.

class Test {
    public static void main(String[] args) {
        Runnable task = () -> {
            boolean run = true;
            while (run) {
                System.out.println("running");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        List<Thread> threads = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
            Thread t = new Thread(task);
            t.setName("test-" + i);
            t.start();
            threads.add(t);
        }
    }
}

Another way to do it is using join, which will explicitly wait for the threads to finish before the program can complete.

for (Thread t : threads) {
    try {
        t.join();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

While there are any active threads running the program will not finish.

Bojan Petkovic
  • 2,406
  • 15
  • 26