How do you simulate real concurrent requests in Java?

Keywords: Java IE

Sometimes it is necessary to test the concurrency performance of a function without resorting to other tools. It is most convenient to have a concurrent request in your own development language.

Simulating concurrent requests in Java is naturally very convenient, as long as you open a few more threads, it is good to initiate requests. However, such requests will generally have the order of start-up, which is not really concurrent at the same time! __________ How can we achieve real concurrency at the same time? This is what I want to say in this article. Closed CountDownLatch is provided in Java, and it's just the right thing to do.

It only needs:

Open n threads, add a latch, open all threads;
When all threads are ready, press the Open button and you can actually initiate concurrent requests.

package com.test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;

public class LatchTest {

    public static void main(String[] args) throws InterruptedException {
        Runnable taskTemp = new Runnable() {

       // Note that this is non-thread-safe, pit-free
            private int iCounter;

            @Override
            public void run() {
                for(int i = 0; i < 10; i++) {
                    // Initiate request
//                    HttpClientOp.doGet("https://www.baidu.com/");
                    iCounter++;
                    System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        LatchTest latchTest = new LatchTest();
        latchTest.startTaskAllInOnce(5, taskTemp);
    }

    public long startTaskAllInOnce(int threadNums, final Runnable task) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);
        final CountDownLatch endGate = new CountDownLatch(threadNums);
        for(int i = 0; i < threadNums; i++) {
            Thread t = new Thread() {
                public void run() {
                    try {
                        // Make threads wait here, and when the start door opens, they rush into the entrance together.
                        startGate.await();
                        try {
                            task.run();
                        } finally {
                            // Reduce the closing door by 1, to 0, and you can open the closing door.
                            endGate.countDown();
                        }
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
            };
            t.start();
        }
        long startTime = System.nanoTime();
        System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");
        // Since only one switch is needed to open the door, the start door is opened immediately.
        startGate.countDown();
        // Wait till the end door opens.
        endGate.await();
        long endTime = System.nanoTime();
        System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed.");
        return endTime - startTime;
    }
}

Its execution effect is shown in the following figure:

HttpClientOp tool class, you can use mature toolkits, you can also write a brief access method, refer to the following:

class HttpClientOp {
    public static String doGet(String httpurl) {
        HttpURLConnection connection = null;
        InputStream is = null;
        BufferedReader br = null;
        String result = null;// Returns the result string
        try {
            // Create remote url connection objects
            URL url = new URL(httpurl);
            // Open a connection through a remote url connection object and force it to the httpURLConnection class
            connection = (HttpURLConnection) url.openConnection();
            // Set the connection mode:get
            connection.setRequestMethod("GET");
            // Setting the timeout to connect to the host server: 15000 milliseconds
            connection.setConnectTimeout(15000);
            // Set the time to read the data returned remotely: 60,000 milliseconds
            connection.setReadTimeout(60000);
            // Send request
            connection.connect();
            // Get input stream through connection connection connection
            if (connection.getResponseCode() == 200) {
                is = connection.getInputStream();
                // Encapsulate the input stream is and specify the character set
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                // Storing data
                StringBuffer sbf = new StringBuffer();
                String temp = null;
                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // close resource
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            connection.disconnect();// Close remote connection
        }

        return result;
    }

    public static String doPost(String httpUrl, String param) {

        HttpURLConnection connection = null;
        InputStream is = null;
        OutputStream os = null;
        BufferedReader br = null;
        String result = null;
        try {
            URL url = new URL(httpUrl);
            // Open a connection through a remote url connection object
            connection = (HttpURLConnection) url.openConnection();
            // Setting the connection request mode
            connection.setRequestMethod("POST");
            // Setting the timeout time to connect to the host server: 15000 milliseconds
            connection.setConnectTimeout(15000);
            // Set the time-out for reading host server to return data: 60000 milliseconds
            connection.setReadTimeout(60000);

            // The default value is: false, which needs to be set to true when transferring or writing data to a remote server.
            connection.setDoOutput(true);
            // The default value is: true, which is set to true when reading data to remote services. This parameter is optional.
            connection.setDoInput(true);
            // Set the format of the incoming parameters: The request parameters should be in the form of name1 = value1 & Name2 = value2.
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            // Setting Authorization Information: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0
            connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
            // Getting an output stream by connecting objects
            os = connection.getOutputStream();
            // Write / transfer parameters through the output stream object, which is written through a byte array
            os.write(param.getBytes());
            // Get an input stream by connecting objects and read it remotely
            if (connection.getResponseCode() == 200) {

                is = connection.getInputStream();
                // Wrapping the input stream objects: charset is set according to the requirements of the work item group
                br = new BufferedReader(new InputStreamReader(is, "UTF-8"));

                StringBuffer sbf = new StringBuffer();
                String temp = null;
                // Loop through rows and rows to read data
                while ((temp = br.readLine()) != null) {
                    sbf.append(temp);
                    sbf.append("\r\n");
                }
                result = sbf.toString();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // close resource
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != os) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            // Disconnect from remote address url
            connection.disconnect();
        }
        return result;
    }
}

As above, you can initiate real concurrent requests.

The concurrent request operation flow diagram is as follows:

A door is set up here to ensure that all threads take effect simultaneously. However, the simultaneous start-up here is only at the linguistic level, and it is not absolutely simultaneous. Specific calls also depend on the number of CPU s, threads and the thread scheduling function of the operating system, but we don't need to get bogged down in these, the key is to understand the principle!

Cyclic Barrier, a toolbar with similar functions to CountDownLatch, also provides a way to wait for all threads to arrive at a certain point and then start an action together. But the purpose of the fence is quite pure, that is, to wait for all threads to arrive. The blocking CountDownLatch mentioned above, although it implements all threads to arrive, starts again. His trigger point is actually the last switch, so the emphasis is different.

Take a brief look at how fences achieve real simultaneous concurrency. Examples are as follows:

// Consistent with the locking structure
public class LatchTest {

    public static void main(String[] args) throws InterruptedException {

        Runnable taskTemp = new Runnable() {

            private int iCounter;

            @Override
            public void run() {
                // Initiate request
//              HttpClientOp.doGet("https://www.baidu.com/");
                iCounter++;
                System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);
            }
        };

        LatchTest latchTest = new LatchTest();
//        latchTest.startTaskAllInOnce(5, taskTemp);
        latchTest.startNThreadsByBarrier(5, taskTemp);
    }

    public void startNThreadsByBarrier(int threadNums, Runnable finishTask) throws InterruptedException {
        // Setting the action when the fence is lifted, such as initializing certain values
        CyclicBarrier barrier = new CyclicBarrier(threadNums, finishTask);
        // Start n threads, consistent with the fence threshold, that is, when the number of threads ready to meet the requirements, the fence just opens, so as to achieve a unified control effect.
        for (int i = 0; i < threadNums; i++) {
            Thread.sleep(100);
            new Thread(new CounterTask(barrier)).start();
        }
        System.out.println(Thread.currentThread().getName() + " out over...");
    }
}

class CounterTask implements Runnable {

    // Enter the fence, generally in a more elegant way
    private CyclicBarrier barrier;

    public CounterTask(final CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " is ready...");
        try {
            // Set up a fence so that when waiting here, the thread arriving at the position can open the door when it meets the requirement.
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " started...");
    }
}

Its operation results are as follows:

Each has its own application scenario, the key is the requirement. As far as the requirements of the examples in this article are concerned, individuals prefer to use latches because they are more controllable. But the code is too much, so see if you like it! ___________

Posted by Spikey on Sun, 03 Feb 2019 12:12:16 -0800