面试题答案
一键面试- 使用
Thread.sleep
方法:- 原理:通过让发送线程睡眠一定时间,来控制数据发送的频率,从而限制发送速率。
- 示例代码:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
public class RateLimitedSocketSender {
private static final int BYTES_PER_SECOND = 1024; // 假设每秒发送1024字节
private static final int SLEEP_TIME_PER_BYTE = (int) (1000.0 / BYTES_PER_SECOND);
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 12345);
OutputStream outputStream = socket.getOutputStream()) {
byte[] data = "Hello, World!".getBytes();
for (byte b : data) {
outputStream.write(b);
Thread.sleep(SLEEP_TIME_PER_BYTE);
}
outputStream.flush();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
- 使用
Timer
和TimerTask
:- 原理:
Timer
类用于安排在后台线程中执行任务,TimerTask
是要执行的任务。通过定期执行发送任务,控制数据发送速率。 - 示例代码:
- 原理:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
public class RateLimitedSocketSenderWithTimer {
private static final int BYTES_PER_SECOND = 1024;
private static final int PERIOD = 1000; // 1秒
private static final int DELAY = 0;
private static int byteIndex = 0;
private static byte[] data = "Hello, World!".getBytes();
private static OutputStream outputStream;
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 12345)) {
outputStream = socket.getOutputStream();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
if (byteIndex < data.length) {
try {
outputStream.write(data[byteIndex++]);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
} else {
timer.cancel();
}
}
}, DELAY, PERIOD / BYTES_PER_SECOND);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 使用
ScheduledExecutorService
:- 原理:
ScheduledExecutorService
提供了在给定延迟后执行任务,或者定期执行任务的功能。 - 示例代码:
- 原理:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class RateLimitedSocketSenderWithScheduledExecutor {
private static final int BYTES_PER_SECOND = 1024;
private static final int PERIOD = 1000;
private static int byteIndex = 0;
private static byte[] data = "Hello, World!".getBytes();
private static OutputStream outputStream;
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 12345)) {
outputStream = socket.getOutputStream();
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> {
if (byteIndex < data.length) {
try {
outputStream.write(data[byteIndex++]);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
} else {
executor.shutdown();
}
}, 0, PERIOD / BYTES_PER_SECOND, TimeUnit.MILLISECONDS);
} catch (IOException e) {
e.printStackTrace();
}
}
}
在实际应用中,还可以考虑使用更复杂的算法,如令牌桶算法来实现更精确的流量控制。令牌桶算法可以通过java.util.concurrent
包下的Semaphore
类模拟实现,它允许一定数量的线程同时访问资源,在流量控制场景下,可以将“资源”看作是允许发送的数据量。例如:
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.Semaphore;
public class TokenBucketRateLimiter {
private static final int CAPACITY = 1024; // 令牌桶容量,即最大允许发送的字节数
private static final int REFILL_RATE = 1024; // 每秒填充的令牌数
private static final Semaphore semaphore = new Semaphore(CAPACITY);
private static final Thread refillThread;
static {
refillThread = new Thread(() -> {
while (true) {
try {
semaphore.release(REFILL_RATE);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
refillThread.setDaemon(true);
refillThread.start();
}
public static void main(String[] args) {
try (Socket socket = new Socket("localhost", 12345);
OutputStream outputStream = socket.getOutputStream()) {
byte[] data = "Hello, World!".getBytes();
for (byte b : data) {
semaphore.acquire();
outputStream.write(b);
}
outputStream.flush();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
上述代码中,Semaphore
的acquire
方法会尝试获取一个令牌,如果没有可用令牌则等待。refillThread
线程以每秒REFILL_RATE
个令牌的速率向令牌桶中添加令牌。这样就通过令牌桶算法实现了流量控制。