java线程的全局变量同步

时间:2014-07-17 13:27:41

标签: java multithreading synchronization

我正在写java的性能测试。 主要思想是创建方法loadTest(String url,int threadNumber,int requestNumber),它将返回成功请求的总时间和成功请求的数量。 但我仍然坚持返回数据。

现在如果我运行此测试 MyRunnable.getTotalTime()和MyRunnable.getCountSuccessRequest()返回0; (控制台中的第一个字符串 并在控制台的输出结束时更正值。 我希望他们回来

我如何才能返回正确的值?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestMain {

public static void main(String [] args) {
    System.out.println("Start test.");
    TestMain test = new TestMain();
    test.loadTest("http://www.google.com/search?q=java", 10, 50); // will execute 50 get request in 10 threads.
}

public void loadTest(String site, int threadNumber, int requestNumber) {
    ExecutorService executor = Executors.newFixedThreadPool(threadNumber);
    for ( int i = 0; i < requestNumber; i++ ) {
        Runnable worker = new MyRunnable(site);
        executor.execute(worker); 
    }
//System.out.println("Total time " + MyRunnable.getTotalTime() + " total success request = " + MyRunnable.getCountSuccessRequest() );//still not work
}
}

班级

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class MyRunnable implements Runnable {
    private static final String USER_AGENT = "Chrome";
    private  static int count = 1;
    private  static int countSuccessRequest = 0;
    private static long totalTime ;
    private final String url;
    public MyRunnable(String url) {
        this.url = url;
    }
    //<________edit__________>
    SyncronizedCounter sc = new SyncronizedCounter();
    //<________edit__________>

    public static long getTotalTime() {
        return totalTime;
    }
    public static int getCountSuccessRequest() {
        return countSuccessRequest;
    }

@Override
public void run() {
    try {
        URL obj = new URL(url);
        HttpURLConnection con =  (HttpURLConnection) obj.openConnection();
        con.setRequestMethod("GET");
        con.setRequestProperty("User-Agent", USER_AGENT); 
        long startTime = System.currentTimeMillis();
        int responseCode = con.getResponseCode();
        long elapseTime = System.currentTimeMillis() - startTime;
        totalTime += elapseTime;
        count++;
        if (responseCode == 200 ) {
            countSuccessRequest ++;
        }
        //<________edit__________>
        sc.incrementTime(elapseTime);
        //<________edit__________>
        System.out.println("\nSending 'GET' request to URL : " + url );
        System.out.println("Response Code : " + responseCode);
        System.out.println("Response time : " + elapseTime + " milliseconds\n");
        System.out.println("----Total time for all request = " + totalTime + " mileseconds");
        System.out.println("----Total count of request =  " + count);
        System.out.println("----Count of success request =  " + countSuccessRequest);
        in.close();
    } catch (Exception e) {
    }
}
 //<________edit__________>
 public class SyncronizedCounter{
    private  long totalTime1 = 0;

    public synchronized void incrementTime( long time){
        totalTime1 += time;
    }

    public synchronized long getTime() {
        return totalTime1;
    }
}
//<________edit__________>
}

我的附加编辑标记为&lt; ______编辑________ &gt;。我在方法run()中添加了包装类和实例。我怎样才能获得TestMain.class中的最后一个值

3 个答案:

答案 0 :(得分:0)

创建一个胶囊对象Stats(它必须是非原始的)并给它方法

public synchronized void addTotalTime(long elapsed)
public synchronized void addSuccessCount(long elapsed)
public synchronized void addTotalCount(long elapsed)

这确保了不会同时执行+=次操作,因此没有竞争条件会将不确定性置于将存储到变量中的值中。 有关技术细节,请参阅here


代码存根看起来像这样

private class Stats {
    private int success, total;
    private long time;

    public synchronized void addTotalTime(long elapsed) { time += elapsed; }
    public synchronized void addSuccesCount(int c) { sucess += c; }
    public synchronized void addTotalCount(int c) { total += c; }

    public long getTotalTime() { return time; }
    public int getSuccessCount() { return success; }
    public int getTotalCount() { return total; }
}
Stats s = new Stats();
// in worker:
s.add...(...)
// at end:
System.out.println(s.get...());

答案 1 :(得分:0)

我建议使用executor.submit(yourRunnable),并且您的主题将返回他们作为Future<Integer>工作的时间。之后只需总结所有退回的期货。

答案 2 :(得分:0)

假设你想要一个运行计数,而不仅仅是最后的总数,我会使用ReentrantLock来处理更新,并使用Callables和Futures而不是Runnable来处理http请求。

一般情况下,最好避免显式同步,并尽可能依赖java.util.concurrent。

以下是Java 6/7和Java 8中的示例解决方案。

Java 6/7

public class TestMainJdk7 {

    public static void main(final String... args) throws Exception {

        final TestRun testRun = new TestRun("http://www.google.com/search?q=java", "Chrome", 5, 5, 100000L);

        final TestResults testResults = testRun.execute();

        // Uncomment and add Apache commons-math to report quantiles, mean, min, max, standard deviation
        // for (int quantile = 5; quantile <= 100; quantile += 5) {
        // System.out.println(quantile + ": " + testResults.rawDescriptiveStatistics.getPercentile(quantile));
        // }
        // System.out.println("mean: " + testResults.rawDescriptiveStatistics.getMean());
        // System.out.println("min: " + testResults.rawDescriptiveStatistics.getMin());
        // System.out.println("max: " + testResults.rawDescriptiveStatistics.getMax());
        // System.out.println("standard deviation: " + testResults.rawDescriptiveStatistics.getStandardDeviation());

    }

    public static class TestResults {

        private final double[] rawDurations;

        public final TestRun testRun;

        public final List<TimedHttpRequestResult> timedHttpRequestResults;

        // Uncomment and add Apache commons-math to report quantiles, mean, min, max, standard deviation
        // public final org.apache.commons.math.stat.descriptive.DescriptiveStatistics rawDescriptiveStatistics;

        public TestResults(final TestRun testRun, final List<TimedHttpRequestResult> timedHttpRequestResults) {

            this.testRun = testRun;

            this.timedHttpRequestResults = timedHttpRequestResults;

            final List<Long> successfulDurations = new ArrayList<Long>();

            for (final TimedHttpRequestResult timedHttpRequestResult : timedHttpRequestResults) {

                if (timedHttpRequestResult.isSuccess()) {
                    successfulDurations.add(timedHttpRequestResult.duration);
                }
            }

            this.rawDurations = new double[successfulDurations.size()];

            for (int i = 0; i < successfulDurations.size(); i++) {
                this.rawDurations[i] = successfulDurations.get(i);
            }

            // Uncomment and add Apache commons-math to report quantiles, mean, min, max, standard deviation
            // this.rawDescriptiveStatistics = new org.apache.commons.math.stat.descriptive.DescriptiveStatistics(
            // this.rawDurations);
        }

    }

    public static class TestRun {
        public final String urlString;
        public final String userAgentString;
        public final int threadNumber;
        public final int requestNumber;
        public final long timeoutInMillis;

        private long totalDurationSoFar;
        private int totalRunSoFar;
        private int totalSuccessfulSoFar;

        private final ReentrantLock lock = new ReentrantLock();

        public TestRun(final String urlString, final String userAgentString, final int threadNumber,
                final int requestNumber, final long timeoutInMillis) {
            this.urlString = urlString;
            this.threadNumber = threadNumber;
            this.requestNumber = requestNumber;
            this.userAgentString = userAgentString;
            this.timeoutInMillis = timeoutInMillis;
        }

        public TestRunStatus updateCounts(final long duration, final int responseCode) {
            this.lock.lock();
            try {
                this.totalDurationSoFar = this.totalDurationSoFar + duration;

                this.totalRunSoFar++;

                if (responseCode == HttpURLConnection.HTTP_OK) {
                    this.totalSuccessfulSoFar++;

                }
                return this.currentStatus();
            } finally {
                this.lock.unlock();
            }

        }

        public TestRunStatus currentStatus() {
            return new TestRunStatus(this.totalDurationSoFar, this.totalRunSoFar, this.totalSuccessfulSoFar);
        }

        public TestResults execute() throws InterruptedException {

            final ExecutorService executor = Executors.newFixedThreadPool(this.threadNumber);

            final Map<TimedHttpRequest, Future<TimedHttpRequestResult>> timedHttpRequestFuturesByRequest = new HashMap<TimedHttpRequest, Future<TimedHttpRequestResult>>();

            for (int i = 0; i < this.requestNumber; i++) {

                final TimedHttpRequest timedHttpRequest = new TimedHttpRequest(this);

                timedHttpRequestFuturesByRequest.put(timedHttpRequest, executor.submit(timedHttpRequest));
            }

            final List<TimedHttpRequestResult> timedHttpRequestResults = new ArrayList<TimedHttpRequestResult>();

            for (final Map.Entry<TimedHttpRequest, Future<TimedHttpRequestResult>> timedHttpRequestFuture : timedHttpRequestFuturesByRequest.entrySet()) {
                final TimedHttpRequest timedHttpRequest = timedHttpRequestFuture.getKey();
                try {

                    timedHttpRequestResults.add(timedHttpRequestFuture.getValue()
                        .get(this.timeoutInMillis, TimeUnit.MILLISECONDS));

                } catch (final Exception e) {
                    timedHttpRequestResults.add(new TimedHttpRequestResult(timedHttpRequest, e,
                            this.updateCounts(0, -1)));
                }
            }

            executor.shutdown();

            return new TestResults(this, timedHttpRequestResults);

        }

    }

    public static class TestRunStatus {
        public final long totalDurationSoFar;

        public final int totalRunSoFar;

        public final int totalSuccessfulSoFar;

        TestRunStatus(final long totalDurationSoFar, final int totalRunSoFar, final int totalSuccessfulSoFar) {
            this.totalDurationSoFar = totalDurationSoFar;
            this.totalRunSoFar = totalRunSoFar;
            this.totalSuccessfulSoFar = totalSuccessfulSoFar;
        }
    }

    public static class TimedHttpRequestResult {

        //@formatter:off
        public static final String TO_STRING_FORMAT = "\nSending 'GET' request to URL : %s"
                + "\nResponse Code : %s"
                + "\nResponse time:  %s"
                + "\n---- Total time for all request = %s milliseconds"
                + "\n---- Total count of request =  %s"
                + "\n---- Count of success request =  %s";
        //@formatter:on

        public final TimedHttpRequest timedHttpRequest;

        public final long duration;

        public final int responseCode;

        public final Exception exception;

        public final TestRunStatus testRunStatus;

        public TimedHttpRequestResult(final TimedHttpRequest timedHttpRequest, final long duration,
                final int responseCode, final TestRunStatus testRunStatus) {
            this.timedHttpRequest = timedHttpRequest;
            this.duration = duration;
            this.responseCode = responseCode;
            this.testRunStatus = testRunStatus;
            this.exception = null;
        }

        public TimedHttpRequestResult(final TimedHttpRequest timedHttpRequest, final Exception exception,
                final TestRunStatus testRunStatus) {
            this.timedHttpRequest = timedHttpRequest;
            this.duration = -1L;
            this.responseCode = -1;
            this.testRunStatus = testRunStatus;
            this.exception = exception;
        }

        boolean isSuccess() {
            return this.responseCode == HttpURLConnection.HTTP_OK;
        }

        @Override
        public String toString() {
            return String.format(TO_STRING_FORMAT, this.timedHttpRequest.testRun.urlString, this.responseCode,
                    this.duration, this.testRunStatus.totalDurationSoFar, this.testRunStatus.totalRunSoFar,
                    this.testRunStatus.totalSuccessfulSoFar);
        }
    }

    public static class TimedHttpRequest implements Callable<TimedHttpRequestResult> {

        public final TestRun testRun;

        public TimedHttpRequest(final TestRun testRun) {
            this.testRun = testRun;
        }

        @Override
        public TimedHttpRequestResult call() throws Exception {
            final URL url = new URL(this.testRun.urlString);

            final HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setRequestProperty("User-Agent", this.testRun.userAgentString);

            final long startTime = System.nanoTime();

            final int responseCode = httpURLConnection.getResponseCode();

            final long endTime = System.nanoTime();

            httpURLConnection.disconnect();

            final long duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);

            final TimedHttpRequestResult timedHttpRequestResult = new TimedHttpRequestResult(this, duration,
                    responseCode, this.testRun.updateCounts(duration, responseCode));

            System.out.println(timedHttpRequestResult);

            return timedHttpRequestResult;
        }

    }
}

Java 8

public class TestMainJdk8 {

    public static void main(final String... args) throws Exception {

        final TestRun testRun = new TestMainJdk8.TestRun("http://www.google.com/search?q=java", "Chrome", 5, 5,
                100000L);

        final TestResults testResults = testRun.execute();

        // Uncomment and add Apache commons-math to report quantiles, mean, min, max, standard deviation
        // for (int quantile = 5; quantile <= 100; quantile += 5) {
        // System.out.println(quantile + ": " + testResults.rawDescriptiveStatistics.getPercentile(quantile));
        // }
        // System.out.println("mean: " + testResults.rawDescriptiveStatistics.getMean());
        // System.out.println("min: " + testResults.rawDescriptiveStatistics.getMin());
        // System.out.println("max: " + testResults.rawDescriptiveStatistics.getMax());
        // System.out.println("standard deviation: " + testResults.rawDescriptiveStatistics.getStandardDeviation());

    }

    public static class TestResults {

        private final double[] rawDurations;

        public final TestRun testRun;

        public final List<TimedHttpRequestResult> timedHttpRequestResults;

        // Uncomment to report quantiles, mean, min, max, standard deviation
        // public final org.apache.commons.math.stat.descriptive.DescriptiveStatistics rawDescriptiveStatistics;

        public TestResults(final TestRun testRun, final List<TimedHttpRequestResult> timedHttpRequestResults) {

            this.testRun = testRun;

            this.timedHttpRequestResults = timedHttpRequestResults;

            this.rawDurations = timedHttpRequestResults.stream()
                    .filter(TimedHttpRequestResult::isSuccess)
                    .mapToDouble(timedHttpRequestResult -> timedHttpRequestResult.duration)
                    .toArray();

            // Uncomment and add Apache commons-math to report quantiles, mean, min, max, standard deviation
            // this.rawDescriptiveStatistics = new org.apache.commons.math.stat.descriptive.DescriptiveStatistics(
            // this.rawDurations);
        }

    }

    public static class TestRun {

        public final String urlString;

        public final String userAgentString;

        public final int threadNumber;

        public final int requestNumber;

        public final long timeoutInMillis;

        private long totalDurationSoFar;

        private int totalRunSoFar;

        private int totalSuccessfulSoFar;

        private final ReentrantLock lock = new ReentrantLock();

        public TestRun(final String urlString, final String userAgentString, final int threadNumber,
                final int requestNumber, final long timeoutInMillis) {
            this.urlString = urlString;
            this.threadNumber = threadNumber;
            this.requestNumber = requestNumber;
            this.userAgentString = userAgentString;
            this.timeoutInMillis = timeoutInMillis;
        }

        public TestRunStatus updateCounts(final long duration, final int responseCode) {
            this.lock.lock();
            try {
                this.totalDurationSoFar = this.totalDurationSoFar + duration;

                this.totalRunSoFar++;

                if (responseCode == HttpURLConnection.HTTP_OK) {
                    this.totalSuccessfulSoFar++;

                }
                return this.currentStatus();
            } finally {
                this.lock.unlock();
            }

        }

        public TestRunStatus currentStatus() {
            return new TestRunStatus(this.totalDurationSoFar, this.totalRunSoFar, this.totalSuccessfulSoFar);
        }

        public TestResults execute() throws InterruptedException {

            final ExecutorService executor = Executors.newFixedThreadPool(this.threadNumber);

            final Map<TimedHttpRequest, Future<TimedHttpRequestResult>> timedHttpRequestFuturesByRequest = IntStream.range(
                    1, this.requestNumber)
                    .mapToObj(index -> new TimedHttpRequest(this))
                    .collect(Collectors.toMap(Function.identity(), timedHttpRequest -> executor.submit(timedHttpRequest)));

            final List<TimedHttpRequestResult> timedHttpRequestResults = timedHttpRequestFuturesByRequest.entrySet()
                    .stream()
                    .map(entry -> {

                        final TimedHttpRequest timedHttpRequest = entry.getKey();

                        try {

                            return entry.getValue()
                                    .get(this.timeoutInMillis, TimeUnit.MILLISECONDS);

                        } catch (final Exception e) {
                            return new TimedHttpRequestResult(timedHttpRequest, e, this.updateCounts(0, -1));
                        }
                    })
                    .collect(Collectors.toList());

            executor.shutdown();

            return new TestResults(this, timedHttpRequestResults);

        }
    }

    public static class TestRunStatus {

        public final long totalDurationSoFar;

        public final int totalRunSoFar;

        public final int totalSuccessfulSoFar;

        TestRunStatus(final long totalDurationSoFar, final int totalRunSoFar, final int totalSuccessfulSoFar) {
            this.totalDurationSoFar = totalDurationSoFar;
            this.totalRunSoFar = totalRunSoFar;
            this.totalSuccessfulSoFar = totalSuccessfulSoFar;
        }
    }

    public static class TimedHttpRequestResult {

        //@formatter:off
        public static final String TO_STRING_FORMAT = "\nSending 'GET' request to URL : %s"
                + "\nResponse Code : %s"
                + "\nResponse time:  %s"
                + "\n---- Total time for all request = %s milliseconds"
                + "\n---- Total count of request =  %s"
                + "\n---- Count of success request =  %s";
        //@formatter:on

        public final TimedHttpRequest timedHttpRequest;

        public final long duration;

        public final int responseCode;

        public final Exception exception;

        public final TestRunStatus testRunStatus;

        public TimedHttpRequestResult(final TimedHttpRequest timedHttpRequest, final long duration,
                final int responseCode, final TestRunStatus testRunStatus) {
            this.timedHttpRequest = timedHttpRequest;
            this.duration = duration;
            this.responseCode = responseCode;
            this.testRunStatus = testRunStatus;
            this.exception = null;
        }

        public TimedHttpRequestResult(final TimedHttpRequest timedHttpRequest, final Exception exception,
                final TestRunStatus testRunStatus) {
            this.timedHttpRequest = timedHttpRequest;
            this.duration = -1L;
            this.responseCode = -1;
            this.testRunStatus = testRunStatus;
            this.exception = exception;
        }

        boolean isSuccess() {
            return this.responseCode == HttpURLConnection.HTTP_OK;
        }

        @Override
        public String toString() {
            return String.format(TO_STRING_FORMAT, this.timedHttpRequest.testRun.urlString, this.responseCode,
                    this.duration, this.testRunStatus.totalDurationSoFar, this.testRunStatus.totalRunSoFar,
                    this.testRunStatus.totalSuccessfulSoFar);
        }
    }

    public static class TimedHttpRequest implements Callable<TimedHttpRequestResult> {

        public final TestRun testRun;

        public TimedHttpRequest(final TestRun testRun) {
            this.testRun = testRun;
        }

        @Override
        public TimedHttpRequestResult call() throws Exception {
            final URL url = new URL(this.testRun.urlString);

            final HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setRequestProperty("User-Agent", this.testRun.userAgentString);

            final long startTime = System.nanoTime();

            final int responseCode = httpURLConnection.getResponseCode();

            final long endTime = System.nanoTime();

            httpURLConnection.disconnect();

            final long duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);

            final TimedHttpRequestResult timedHttpRequestResult = new TimedHttpRequestResult(this, duration,
                    responseCode, this.testRun.updateCounts(duration, responseCode));

            System.out.println(timedHttpRequestResult);

            return timedHttpRequestResult;
        }
    }
}