简体   繁体   English

及时测量数量[Java,多线程]

[英]Measure amount in time[Java, Multithreading]

What is the best practice to measure amount in time?及时测量数量的最佳做法是什么? I have a multithreading application.我有一个多线程应用程序。 Thread can be any number.线程可以是任意数字。 I want to perform N times operation per second.我想每秒执行 N 次操作。 I tried several techniques but still have no 100% success.我尝试了几种技术,但仍然没有 100% 成功。 Here is a snippet and you might see the problem more clearly.这是一个片段,您可能会更清楚地看到问题。

To be more clear I want to send max 100 messages within one second(1000 millis).更清楚地说,我想在一秒钟(1000 毫秒)内发送最多 100 条消息。 Eg If those threads are able to do it within 450 millis then I want to force all threads to wait 550 millis and then do the same operation again and again.例如,如果这些线程能够在 450 毫秒内完成,那么我想强制所有线程等待 550 毫秒,然后一次又一次地执行相同的操作。 I call this speedLimitMetter.getWaitTime() from threads.我从线程中调用这个 speedLimitMetter.getWaitTime()。 If it gives X > 0 then I force thread to wait X millis.如果它给出 X > 0,那么我强制线程等待 X 毫秒。

Any hint will be helpful任何提示都会有所帮助

public class SpeedLimitMeter {
    private int speedLimit;

    private volatile int messageCounter = 0;

    private volatile long firstTime = 0;
    private volatile long lastTime = 0;

    private volatile long waitTime = 0;
    private volatile long waitUntil = 0;

    public SpeedLimitMeter(int speedLimit) {
        this.speedLimit = speedLimit;
    }

    public synchronized long getWaitTime() {
        long currTime = System.currentTimeMillis();

        if (messageCounter == speedLimit) {
            if (waitTime == 0) {
                long elapedTime = currTime - firstTime;
                if (elapedTime < 1000) {
                    waitTime = 1000 - elapedTime;
                    waitUntil = currTime + waitTime;

                    return waitTime;
                }

                reset();
            } else if (currTime < waitUntil) {
                return waitTime;
            } else {
                reset();
            }

        }

        if (messageCounter == 0) firstTime = currTime;
        lastTime = currTime;

        messageCounter++;

        return 0;
    }

    private synchronized void reset() {
        firstTime = 0;
        lastTime = 0;
        waitTime = 0;
        waitUntil = 0;
        messageCounter = 0;
    }

}

I recomment taking a look at the functionalities provided by ScheduledThreadPoolExecutor ( https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html ) as whatever it exactly is you are trying to do, it might be solvable that way, in a proper way. I recomment taking a look at the functionalities provided by ScheduledThreadPoolExecutor ( https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html ) as whatever it exactly is you are trying to do ,它可能是可以解决的,以适当的方式。

ScheduledThreadPoolExecutor allows you to schedule the periodic execution of a job. ScheduledThreadPoolExecutor 允许您安排作业的定期执行。 You could, eg use this to release a semaphore, if (and only if) that semaphore is depleted.例如,当(且仅当)信号量耗尽时,您可以使用它来释放信号量。 This way, the threads you are trying to regulate could draw a lease, instead of requesting a wait time.这样,您尝试调节的线程可以获取租约,而不是请求等待时间。 It is much more clean this way (imho).这种方式更干净(恕我直言)。

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class ThreadLeaser {
    
    private final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
    private final Semaphore semaphore;
    private final long nanosecondsPause;
    
    public ThreadLeaser(float leasesPerSecond, boolean fair) {
        this.nanosecondsPause = Math.round(1_000_000_000f / leasesPerSecond);
        this.semaphore = new Semaphore(0, fair);
        
        Runnable semRelease = () -> {
            if (this.semaphore.availablePermits() == 0) {
                this.semaphore.release();
            }
        };
        executor.scheduleAtFixedRate(semRelease, 0, nanosecondsPause, TimeUnit.NANOSECONDS);
    }
    
    public void drawNextAvailableLease() throws InterruptedException {
        semaphore.acquire();
    }
}

Be aware, that this solution is not perfectly precise, because if a thread has just acquired a lease and then the release happens immediately after, then the next thread might acquire right away.请注意,此解决方案并不完全精确,因为如果一个线程刚刚获得租约,然后立即释放,那么下一个线程可能会立即获得。 This does not guarantee a 'time distance' but rather guarantees a somewhat constant frequency, if the threads try to acquire regularly enough.如果线程尝试足够有规律地获取,这并不能保证“时间距离”,而是保证某种恒定的频率。

Also, the thing needs some fleshing out (a proper way to terminate and so on), but I leave this to you.此外,这件事需要一些充实(一种适当的终止方式等等),但我把这个留给你。

This testcase shows the behavior (and roughly the precision), for a high supply of waiting threads, started in order (with a tiny waiting period in between).这个测试用例显示了行为(以及大致的精度),对于大量等待线程,按顺序启动(中间有一小段等待时间)。

import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import thread.ThreadLeaser;

public class ThreadLeaseTester {
    @Test
    public void test_tpl() {
        final ThreadLeaser leaser = new ThreadLeaser(5f, true);
        
        List<Thread> toJoin = new ArrayList<>();
        
        long start = System.nanoTime();
        
        for (int i = 0; i < 50; ++i) {
            final int d = i;
            
            try {
                Thread.sleep(5);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            
            Thread thread = new Thread() {
                public void run() {
                    try {
                        leaser.drawNextAvailableLease();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    System.out.println("Thread: " + d + " Time: " + ((System.nanoTime() - start) / 1_000_000) + "ms");
                }
            };
            thread.start();
            toJoin.add(thread);
        }
        
        toJoin.forEach(t -> {
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        assertTrue(toJoin.size() == 100);
    }
}

If you want to perform N operations within a given period of time, without knowing, how much time an operation needs to terminate, you have to start each operation in its own thread.如果你想在给定的时间内执行 N 次操作,而不知道一个操作需要多长时间终止,你必须在它自己的线程中启动每个操作。 When more than N operations may be active any given time, you do not need to limit the number of threads (although this might kill your system, if the execution time is long compared with the given period of time).当任何给定时间可能有超过 N 个操作处于活动状态时,您不需要限制线程数(尽管这可能会杀死您的系统,如果执行时间与给定时间段相比较长)。

The shortest time you can handle is more or less a millisecond for a single operation, but only theoretically, because of thread creation and ramp up times.对于单个操作,您可以处理的最短时间或多或少是一毫秒,但这只是理论上的,因为线程创建和加速时间。 Nevertheless, the pattern remains the same …然而,模式保持不变……

final var maxOperationsPerPeriod = …; // your N
final var lengthOfPeriod = 1000L; // the period of time in ms
final var waitTime = lengthOfPeriod / maxOperationsPerPeriod;

Thread thread;
while( (thread = getNextOperation()) != null )
{
    thread.start();
    thread.sleep( waitTime );
}

I omitted the mandatory exception handling because of personal laziness … apart from that, the code should do the job, when getNextOperation() returns the prepared thread for the next operation, or null when there is no more work.由于个人懒惰,我省略了强制异常处理……除此之外,代码应该完成这项工作,当getNextOperation()返回准备好的线程以进行下一个操作时,或者当没有更多工作时返回null

And each thread must terminate automatically after the operation is done.并且每个线程必须在操作完成后自动终止。

Possible optimisations are to use a thread pool and/or more sophisticated concurrent APIs (like Future s as the return value of getNextOperation() instead of bare Thread s).可能的优化是使用线程池和/或更复杂的并发 API(例如Future s 作为getNextOperation()的返回值而不是裸Thread s)。

And "100%" can be achieved only, if N divides 1000 without a remainder … but as the timing of the JVM is always "unreliable" when you go down to such a high frequency, even that will not work.并且“100%”只能实现,如果 N 除以 1000 没有余数……但是当 go 降到如此高的频率时,JVM 的时序总是“不可靠”的,即使这样也行不通。

Going to 100 operations within an hour would work with that 100% exactness...在一小时内进行 100 次操作将具有 100% 的准确性……

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM