【问题标题】:Syncronization of global variable for java threadsjava线程的全局变量同步
【发布时间】:2014-07-18 10:10:35
【问题描述】:

我正在编写 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__________>
}

我的附加编辑标记为 ______edit________>。我在方法 run() 中添加了包装类和实例。如何获得 TestMain.class 中的最后一个值

【问题讨论】:

    标签: java multithreading synchronization


    【解决方案1】:

    我建议使用executor.submit(yourRunnable),您的线程将返回它们作为Future&lt;Integer&gt; 工作的时间。之后只需总结所有返回的 Futures。

    【讨论】:

    • 但是,我只想获得成功请求的时间。不适合所有人。谢谢。
    【解决方案2】:

    创建一个胶囊对象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...());
    

    【讨论】:

    • 这是我的主要问题。它应该如何工作?
    • 你卡在哪里了?如何创建一个类?这些方法怎么写?如何访问这些方法?
    • 这是我的主要问题。它应该如何工作?创建新类 SyncronizedCounter{ private long totalTimeMy = 0; public syncronized void incrementTime(long time){ totalTimeMy += time;} public syncronized void getTime(){ return totalTimeMy;}}。比我应该在 MyRunnable 中创建 Instantce 并在方法运行中更改它。但我如何获得最后的价值。我不知道如何在 TestMain.class 中执行此操作。
    • @user3737721 只需为这些值添加 getter。请参阅我对工人阶级和示例电话的编辑。
    • @user3737721 通过调用您最喜欢的输出方法?要获得该值,您只需在整个程序中保持 Stats 对象为静态,或者将其保存在 MainClass 中,并在初始化期间为您的自定义 Runnable 提供一个 Stats 实例。
    【解决方案3】:

    假设您想要一个运行计数,而不仅仅是最后的总数,我将使用 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;
            }
        }
    }
    

    【讨论】:

    • 但最后我只需要总计。)我会检查你的代码谢​​谢。
    • 如果是这种情况,请使用 AtomicInteger.increment 作为请求计数和成功计数,使用 AtomicLong.getAndAdd(duration) 作为总持续时间。
    • 需要 RentrantLock 以允许在单个事务中更改所有值。
    猜你喜欢
    • 2016-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 1970-01-01
    • 2018-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多