Sadly some people couldn't "believe" my results. So please delete my previous answer.
I'm now just posting the code for a client JUnit test, which is quite good
for doing multi-threaded benchmarks, as I think.
Please use this code to test local or remote web resources as you like.
package helloworld;
import java.io.*;
import java.net.*;
import java.util.*;
import org.junit.*;
public class _ParallelFloodHttpRequests {
final static int BUFFER_SIZE = 4096;
final static int N_THREADS = 2;
final List<URL> urls;
final Object globalLock = new Object();
final int[] requestsPerThread = new int[N_THREADS];
final int[] ioExceptionsPerThread = new int[N_THREADS];
public _ParallelFloodHttpRequests()
throws MalformedURLException {
urls = Arrays.asList(new URL[]{
new URL("http://0.0.0.0:8000/YOUR_LINK")
});
}
public final static void inputStreamToOutputStream(InputStream is, OutputStream os)
throws IOException {
BufferedInputStream bis = new BufferedInputStream(is, BUFFER_SIZE);
byte[] buffer = new byte[BUFFER_SIZE];
int nRead;
for (;;) {
nRead = bis.read(buffer);
if (nRead <= 0) { // can also be -1
break;
}
os.write(buffer, 0, nRead);
}
bis.close();
is.close();
}
public final static String urlToString(URL url)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
inputStreamToOutputStream(url.openStream(), baos);
baos.close();
return baos.toString();
}
final class MyThread
implements Runnable {
private final int myId;
private final List<URL> urls;
private int ownRequests = 0;
private int ownIoExceptions = 0;
public MyThread(int myId, List<URL> inputUrls) {
this.myId = myId;
urls = new ArrayList<URL>();
List<URL> tempUrls = new LinkedList<URL>(inputUrls);
Random random = new Random(myId);
for (int i = 0; i < inputUrls.size(); ++i) {
URL url = tempUrls.remove(random.nextInt(tempUrls.size()));
urls.add(url);
}
}
@Override
public void run() {
for (int i = 1; i < Integer.MAX_VALUE; ++i) {
try {
doRequest(urls.get(i % urls.size()));
} catch (IOException e) {
++ownIoExceptions;
}
++ownRequests;
if ((i % 100) == 0) {
synchronized (globalLock) {
requestsPerThread[myId] = ownRequests;
ioExceptionsPerThread[myId] = ownIoExceptions;
}
Thread.yield();
}
}
}
private void doRequest(URL url)
throws IOException {
urlToString(url);
}
}
@Test
public void multiThreaded()
throws MalformedURLException {
Runnable timer = new Runnable() {
@Override
public void run() {
long start = System.currentTimeMillis(), current;
try {
for (;;) { // this is an endless loop, don't you know it?
Thread.sleep(5000);
int globalRequests = 0;
int globalIoExceptions = 0;
synchronized (globalLock) {
for (int i = 0; i < N_THREADS; ++i) {
globalRequests += requestsPerThread[i];
globalIoExceptions += ioExceptionsPerThread[i];
}
System.out.print("requests per thread: ");
for (int i = 0; i < N_THREADS; ++i) {
System.out.print(" #" + i + ": " + requestsPerThread[i]);
}
}
current = System.currentTimeMillis();
int rate =
(int) ((double) globalRequests / ((current - start) /
1000.0));
System.out.print("\n" + rate + " requests / sec, time: " +
((current - start) / 1000) + "s, requests: " +
globalRequests + " ; ");
if (globalIoExceptions > 0) {
System.out.print("IO exceptions so far: " + globalIoExceptions);
}
System.out.println();
}
} catch (InterruptedException e) {
}
}
};
Thread timerThread = new Thread(timer);
Thread[] threads = new Thread[N_THREADS];
for (int i = 0; i < N_THREADS; ++i) {
threads[i] = new Thread(new MyThread(i, urls));
}
timerThread.start();
for (int i = 0; i < N_THREADS; ++i) {
threads[i].start();
}
try {
for (int i = 0; i < N_THREADS; ++i) {
threads[i].join();
}
timerThread.interrupt();
} catch (InterruptedException e) {
}
}
}
Output looks something like:
requests per thread: #0: 11300 #1: 11100
4479 requests / sec, time: 5s, requests: 22400 ;
requests per thread: #0: 23000 #1: 25100
4809 requests / sec, time: 10s, requests: 48100 ;
requests per thread: #0: 35600 #1: 38500
4939 requests / sec, time: 15s, requests: 74100 ;
Please note, that the thread statistics are updated every 100 results
(to prevent too often synchronization)