The way of unit test multithreading solution

Source: Internet
Author: User

Encounter problems

Roost in the development process encountered a problem, there was a service is mass mail, because one send dozens of hundreds, so the use of multi-threaded to operate.

In unit testing, I tuned this method to test the mail sent, the results always appear inexplicable problems, each time not all sent successfully.

Later I felt that the boot of the sub-thread was killed, as if the test method has gone over, try to let the thread sleep at the end of the test method for a few seconds, the result can send the message normally.

Analytical Solutions

Feel this junit a bit fishy, on the Internet to check a bit, and then follow the source, sure enough to find the problem lies.

The main method of Testrunner:

public static void main(String[] args) {    TestRunner aTestRunner = new TestRunner();    try {        TestResult r = aTestRunner.start(args);        if (!r.wasSuccessful()) {            System.exit(1);        }        System.exit(0);    } catch (Exception var3) {        System.err.println(var3.getMessage());        System.exit(2);    }}

As shown above, the System.exit () method is called to close the program, whether successful or not, and this method is used to end the currently running Java Virtual machine.

System.exit (0) is a normal exit program, while System.exit (1) or not 0 indicates an abnormal exit program.

This shows that JUnit is not suitable for testing multi-threaded procedures, but it is not without a method, according to its principle can try to let the main thread block, waiting for other sub-threads to finish before continuing.

The simplest way is to get the main thread to sleep for a few seconds:

TimeUnit.SECONDS.sleep (5);

Review re-offer

In addition to the main thread sleep, there are many other tools to help us solve this problem. Remember today, just try it.

To a database connection pool-related test:

public class multipleconnectiontest{private Hikaridatasource ds;        @Before public void Setup () {hikariconfig config = new Hikariconfig ();        Config.setjdbcurl ("Jdbc:mysql://127.0.0.1:3306/design");        Config.setdriverclassname ("Com.mysql.jdbc.Driver");        Config.setusername ("root");        Config.setpassword ("Fengcs");        Config.setminimumidle (1);        Config.setmaximumpoolsize (5);    ds = new Hikaridatasource (config);    } @After public void teardown () {ds.close ();        } @Test public void Testmulconnection () {Connectionthread connectionthread = new Connectionthread ();        Thread thread = NULL;            for (int i = 0; i < 5; i++) {thread = new thread (connectionthread, "thread-con-" + i);        Thread.Start ();  }//TimeUnit.SECONDS.sleep (5); (1)} Private class Connectionthread implements runnable{@Override public void Run () {Con Nection connection = null;                try {connection = ds.getconnection ();                Statement Statement = Connection.createstatement ();                ResultSet ResultSet = Statement.executequery ("SELECT ID from Tb_user");                String Firstvalue;                System.out.println ("<=============");                System.out.println ("==============>" +thread.currentthread (). GetName () + ":");                    while (Resultset.next ()) {firstvalue = resultset.getstring (1);                System.out.print (firstvalue);            }} catch (SQLException e) {e.printstacktrace ();                    } finally {try {if (connection! = null) {connection.close ();                }} catch (SQLException e) {e.printstacktrace (); }            }        }    }}

This code will get an error as soon as it runs:

Java.sql.SQLException:HikariDataSource Hikaridatasource (HIKARIPOOL-1) has been closed.

1. Using the Join method

Try adding a join directly according to the code above:

@Testpublic void testMulConnection() {    ConnectionThread connectionThread = new ConnectionThread();    Thread thread = null;    for (int i = 0; i < 5; i++) {        thread = new Thread(connectionThread, "thread-con-" + i);        thread.start();        thread.join();    }}

This can be done successfully, but with a closer look, there is no difference between executing a single thread. For the main thread, start one joins one, starts blocking waiting for the child thread to complete, and then loops to start the second operation.

The correct operation should look something like this:

Thread threadA = new Thread(connectionThread);Thread threadB = new Thread(connectionThread);threadA.start();threadB.start();threadA.join();threadB.join();

This allows multiple threads to execute together. But there are many threads, so it's more troublesome to write.

2. Latching-Countdownlatch

Countdownlatch allows one or more threads to wait for another thread to complete the operation.

The Countdownlatch constructor takes a parameter of type int as a counter, and if you want to wait for n points to complete, this will pass in N.

So here it is obvious that the main thread should wait for the other five threads to complete the query before closing. Then add the code (1) and (2) to allow the main thread to block the wait.

private static CountDownLatch latch = new CountDownLatch(5);  // (1)@Testpublic void testMulConnection() throws InterruptedException {    ConnectionThread connectionThread = new ConnectionThread();    Thread thread = null;    for (int i = 0; i < 5; i++) {        thread = new Thread(connectionThread, "thread-con-"+i);        thread.start();    }    latch.await();   // (2)}

When we call Countdownlatch's Countdown method, N will subtract 1,countdownlatch's await method
Blocks the current thread until n becomes 0. Add (3) code, minus one after each thread completes the query.

Private class Connectionthread implements runnable{@Override public void Run () {Connection Connection = nul        L            try {connection = ds.getconnection ();            Statement Statement = Connection.createstatement ();            ResultSet ResultSet = Statement.executequery ("SELECT ID from Tb_user");            String Firstvalue;            System.out.println ("<=============");            System.out.println ("==============>" +thread.currentthread (). GetName () + ":");                while (Resultset.next ()) {firstvalue = resultset.getstring (1);            System.out.print (firstvalue); } latch.countdown ();        (3)} catch (SQLException e) {e.printstacktrace ();                } finally {try {if (connection! = null) {connection.close ();            }} catch (SQLException e) {e.printstacktrace (); }        }    }} 

Test it and fully meet the requirements.

3. Fence-Cyclicbarrier

Cyclicbarrier literally means a barrier (Barrier) that can be recycled (Cyclic). What it's going to do is, let a
When a group thread reaches a barrier (which can also be called a synchronization point), the barrier is blocked until the last thread reaches the barrier.
Open the door, and all the threads blocked by the barrier will continue to run.

This is different from Countdownlatch, but the main thread needs to be blocked, still adding a sync point at the end of the Main method:

private static CyclicBarrier cyclicBarrier = new CyclicBarrier(6);  // (1)@Testpublic void testMulConnection() throws BrokenBarrierException, InterruptedException {    ConnectionThread connectionThread = new ConnectionThread();    Thread thread = null;    for (int i = 0; i < 5; i++) {        thread = new Thread(connectionThread, "thread-con-"+i);        thread.start();    }    cyclicBarrier.await();   // (2)}

Cyclicbarrier the default constructor is cyclicbarrier (int parties), whose parameters represent the number of threads that the barrier intercepts, and each thread calls the await method to tell Cyclicbarrier I have reached the barrier, and then the current thread is blocked.

This time there is no similar blocking countdown method to count, can only rely on the thread to reach the synchronization point to confirm whether all arrived, and other threads do not go to the main method of synchronization point, so also need a five other threads to converge the synchronization point. You can then await at the end of each thread's Run method:

Private class Connectionthread implements runnable{@Override public void Run () {Connection Connection = nul        L            try {connection = ds.getconnection ();            Statement Statement = Connection.createstatement ();            ResultSet ResultSet = Statement.executequery ("SELECT ID from Tb_user");            String Firstvalue;            System.out.println ("<=============");            System.out.println ("==============>" +thread.currentthread (). GetName () + ":");                while (Resultset.next ()) {firstvalue = resultset.getstring (1);            System.out.print (firstvalue);  } cyclicbarrier.await ();        (3)} catch (SQLException e) {e.printstacktrace ();        } catch (Interruptedexception e) {e.printstacktrace ();        } catch (Brokenbarrierexception e) {e.printstacktrace ();   } finally {try {if (connection! = null) {                 Connection.close ();            }} catch (SQLException e) {e.printstacktrace (); }        }    }}

So it feels like there is a potential communication mechanism, both of which are released together. It's only now that six threads are participating in the count, and the Cyclicbarrier constructor should be 6 (less than 6 may also succeed, and more than 61 will always be blocked).

A comprehensive look, I think the most suitable or countdownlatch.

This is mainly to borrow unit test multithreading to deepen the understanding of concurrency-related knowledge points, and use it to practice, to solve some problems. About this unit test multithreading problem Many people should know, at the beginning of leaving the front tried a few people, also asked this question, there are a few said encountered, I asked why there is this problem, how do you solve? None of the results was answered.

In fact, the problem is a good thing, are the opportunity to grow, every problem behind the hidden a lot of blind spots, deep digging down will certainly be fruitful.

The way of unit test multithreading solution

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.