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! ___________