没有以上面的代码为基础
时间: 2025-07-20 21:19:58 浏览: 2
<think>根据用户要求,我们需要完成以下任务:
1. 主类名改为QiangPiao
2. 使用HttpURLConnection代替OkHttp
3. 同时抢三种券(WATER_PURIFIER, DISHWASHER, MICROWAVE_OVEN)
4. 设置抢购时间为北京时间2025年7月17日9:59:00,但程序在8点就启动,等待到点开始抢
5. 每10毫秒抢一次(即每10毫秒对三种券各发起一次请求)
6. 单文件运行
设计思路:
1. 使用ScheduledExecutorService来定时执行任务,因为Timer在并发情况下可能不够精确。
2. 设置一个开始时间(2025-07-17 09:59:00),程序启动后计算需要等待的时间(delayMillis)。
3. 使用一个固定频率的调度任务,每10毫秒触发一次,每次触发同时启动三个线程(或使用线程池)分别抢三种券。
4. 使用HttpURLConnection发送POST请求,设置必要的请求头。
注意:由于抢券请求非常频繁,我们需要考虑服务器压力以及可能被封禁的情况,但用户要求如此。
步骤:
1. 定义目标时间(2025-07-17 09:59:00 北京时间)
2. 计算当前时间到目标时间的毫秒数,作为初始延迟。
3. 创建调度线程池,安排一个固定频率的任务,每10毫秒执行一次。
4. 在这个任务中,我们使用一个线程池(或直接创建三个线程)来同时发送三种券的请求。
由于用户要求单文件,我们将所有代码放在一个类中。
注意:需要处理异常,以及请求的构建。
请求参数:根据原代码,请求体为JSON格式,包含plasSsn(订单号)、userInfo(用户信息)和category(券种类)。
我们将定义三个常量字符串代表三种券,然后循环(或并行)发送。
使用HttpURLConnection的步骤:
- 创建URL对象
- 打开连接(HttpURLConnection)
- 设置请求方法为POST
- 设置请求头(Content-Type, User-Agent等)
- 开启输出,写入请求体
- 获取响应
由于请求非常频繁,我们需要注意连接资源的释放。
代码结构:
- main方法:计算时间,启动调度任务
- 定义一个内部类(或方法)用于发送单个券的请求
- 使用线程池(或直接new Thread)来并发请求三种券
考虑到每10毫秒就要同时发起三个请求,我们使用一个固定大小为3的线程池(每次执行时提交三个任务,然后等待完成?)但是注意,如果前一次请求还没完成,新的请求又来了,这样会累积。因此,我们每次执行时,使用一个线程池(可以重用)来执行这三个任务,但要注意线程池的关闭。或者,我们每次执行时创建三个线程,然后等待它们完成(这样可能阻塞调度线程,导致不精确)。所以,我们使用一个线程池,然后提交任务后不等待(即异步),但要注意任务堆积。
更好的方式:使用一个固定大小的线程池(比如3个线程),每次提交三个任务,然后不阻塞。但是,由于每10毫秒就会提交3个任务,如果任务执行时间较长(比如网络延迟100ms),那么任务会堆积。因此,我们可能需要一个更大的线程池,或者考虑丢弃策略?但用户要求每10ms抢一次,所以即使前一次请求未完成,下一次又开始了。这样会非常快,但可能造成大量并发。
权衡:由于是抢券,我们要求速度,所以可以接受大量并发。但要注意系统资源。
我们可以使用一个缓存线程池(CachedThreadPool),这样线程不够时会创建新的线程。但是,如果请求很多,线程数会爆炸。因此,我们可以限制最大线程数,比如1000?或者使用一个固定大小的线程池(比如100个线程),这样最多同时有100个请求在处理。
但是,为了简单,我们每次执行时启动三个线程(使用new Thread),然后不等待它们结束。这样不会阻塞调度任务,但是线程数量会随着时间线性增长(如果每个请求耗时1秒,那么10秒后就会有3000个线程)。这显然不行。
因此,我们使用一个共享的线程池,该线程池可以处理多个调度周期提交的任务。我们设置一个足够大的线程池(比如固定大小为1000,或者使用无界队列,但这样可能导致内存溢出),或者使用一个工作队列为同步队列的线程池(直接提交,如果线程池满了就抛出异常,但我们不希望抛出异常,所以使用无界队列?)。
考虑到抢券时间不会太长(可能几分钟),我们可以设置一个固定大小的线程池(比如100个线程),这样最多同时处理100个请求,多余的请求会在队列中等待。但这样会导致请求延迟,可能不符合要求。
另一种思路:每10毫秒执行一次,但每次执行时,如果上一次的请求还没完成,就不再发送新的请求(即跳过)。这样避免堆积。但用户要求每10ms抢一次,所以我们可以考虑使用一个信号量控制,如果当前有请求未完成,则跳过本次。但这样会降低频率。
权衡后,我们决定使用一个无界队列的线程池(Executors.newCachedThreadPool()),这样每个任务都会立即执行(除非资源不足)。但要注意,如果请求处理速度跟不上,线程数会持续增长。
由于抢券时间不会持续太久(比如持续1分钟,那么1分钟=60000毫秒,每10ms一次,共6000次,每次3个请求,共18000个请求)。对于现代计算机,18000个线程是可以接受的(但实际可能受限于内存和操作系统限制)。
但是,更合理的方式是使用异步非阻塞IO(NIO),但这里使用HttpURLConnection是阻塞的,所以我们还是用线程池。
我们使用一个共享的线程池(静态的),用于执行所有请求任务。
步骤:
1. 在main方法中创建调度线程池(ScheduledExecutorService)和一个用于执行HTTP请求的线程池(ExecutorService,使用CachedThreadPool)。
2. 计算初始延迟时间。
3. 调度一个固定频率的任务,每10ms执行一次,任务内容:向执行HTTP请求的线程池提交三个任务(分别抢三种券)。
4. 每个抢券任务:使用HttpURLConnection发送请求,然后记录结果。
注意:需要设置请求头,包括User-Agent等。
另外,原代码中有一个订单号(phonePlatSsn)和userInfo,以及URL。我们将其定义为常量。
抢券时间:我们设定一个停止条件,比如抢到券后可以停止,或者持续抢一段时间。但用户没有说明,我们假设一直抢到程序手动停止。
代码实现:
定义常量:
private static final String URL_STR = "http://mkts.chinaums.com/jdhsApi/jdhs/user/category/remain/chance";
private static final String USER_INFO = "3BAF8F6F2A237A9819A8171679320C81BA00BF3D68D2370607B187B1F96E83D910336D47DE4AF1918AC85048A7EE9956DE27E0D03BE230E441CAD6E6E7902100578DB16887CF31C59C36D6F4F6027E6E42F254B1079AEACABC9A9991973B4330480D3BDD836B33B2D6E6F973EF90B54C94457680BCF1C267D6AEA5A4606B6EAD1BA2D7028DA7E58D92A28470EFA17FF1F08849B73EF6A903D8B45C4230F02171800E3E24A345F4DF1C2BEBC6413C92198FF39BF68CA6979F3610FB9909E0C4CC68940C925D30DB1F0ACDDCFFB32F53F979D3F934C0620F19E24D16ECE84748E47D060A743637F23E1BCA1D11FA207B66201E81AA837D7ECD0907F46173A841C2";
private static final String PHONE_PLAT_SSN = "2923fe5dfd054fcab2bc71bf70ffde49";
三种券:使用数组或列表存储:["WATER_PURIFIER", "DISHWASHER", "MICROWAVE_OVEN"]
注意:原代码中,请求体是JSON格式,所以需要构建JSON字符串。
由于我们使用HttpURLConnection,需要将JSON字符串写入输出流。
另外,设置请求头:Content-Type为application/json,以及User-Agent等。
我们定义一个方法:rushCoupon(String category),用于抢指定类型的券。
在rushCoupon方法中:
- 创建URL对象
- 打开连接
- 设置请求方法为POST
- 设置请求头
- 写入请求体(JSON字符串)
- 获取响应码和响应内容
由于是抢券,我们只关心是否成功,所以简单打印结果即可。
开始时间设置:北京时间2025-07-17 09:59:00
注意:程序启动时可能是8点,需要等待到9:59。
计算初始延迟:
long delayMillis = targetTime.toInstant(ZoneOffset.of("+8")).toEpochMilli() - System.currentTimeMillis();
但是,如果当前时间已经超过目标时间,则立即开始。
调度任务:每10毫秒执行一次,所以period=10毫秒。
由于调度任务执行时间可能超过10毫秒,所以使用scheduleAtFixedRate会累积,因此我们使用scheduleWithFixedDelay?但是这里我们希望固定频率,所以使用scheduleAtFixedRate。
另外,我们使用一个单独的线程池来执行调度任务(ScheduledExecutorService),以及一个用于执行HTTP请求的线程池(ExecutorService)。
最后,程序需要一直运行,所以我们在main方法最后加上等待(比如使用CountDownLatch,或者简单的Thread.sleep(Long.MAX_VALUE))。
代码示例:
注意:由于是示例,我们简化错误处理。
由于用户要求单文件,我们将所有代码放在QiangPiao类中。
开始编写代码:</think>我将基于您的要求,对代码进行全面修改和完善。以下是完整的实现方案:
```java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class QiangPiao {
private static final String URL_STR = "http://mkts.chinaums.com/jdhsApi/jdhs/user/category/remain/chance";
private static final String USER_INFO = "3BAF8F6F2A237A9819A8171679320C81BA00BF3D68D2370607B187B1F96E83D910336D47DE4AF1918AC85048A7EE9956DE27E0D03BE230E441CAD6E6E7902100578DB16887CF31C59C36D6F4F6027E6E42F254B1079AEACABC9A9991973B4330480D3BDD836B33B2D6E6F973EF90B54C94457680BCF1C267D6AEA5A4606B6EAD1BA2D7028DA7E58D92A28470EFA17FF1F08849B73EF6A903D8B45C4230F02171800E3E24A345F4DF1C2BEBC6413C92198FF39BF68CA6979F3610FB9909E0C4CC68940C925D30DB1F0ACDDCFFB32F53F979D3F934C0620F19E24D16ECE84748E47D060A743637F23E1BCA1D11FA207B66201E81AA837D7ECD0907F46173A841C2";
private static final String PHONE_PLAT_SSN = "2923fe5dfd054fcab2bc71bf70ffde49";
private static final String[] COUPON_TYPES = {"WATER_PURIFIER", "DISHWASHER", "MICROWAVE_OVEN"};
private static final AtomicBoolean isStarted = new AtomicBoolean(false);
public static void main(String[] args) throws Exception {
// 设置抢购开始时间(2025年7月17日9:59:00 北京时间)
ZonedDateTime targetTime = LocalDateTime.of(2025, 7, 17, 9, 59, 0)
.atZone(ZoneId.of("Asia/Shanghai"));
long startTimeMillis = targetTime.toInstant().toEpochMilli();
System.out.println("程序已启动,等待抢购开始时间...");
System.out.println("目标抢购时间: " + targetTime);
// 创建线程池(3个核心线程 + 调度线程)
ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
// 每10毫秒执行一次抢购任务
executor.scheduleAtFixedRate(() -> {
if (!isStarted.get() && System.currentTimeMillis() >= startTimeMillis) {
isStarted.set(true);
System.out.println("\n===== 抢购开始! =====");
}
if (isStarted.get()) {
// 同时抢三种券
for (String couponType : COUPON_TYPES) {
executor.execute(() -> rushCoupon(couponType));
}
}
}, 0, 10, TimeUnit.MILLISECONDS); // 每10ms触发一次
// 保持程序运行
Thread.sleep(Long.MAX_VALUE);
}
private static void rushCoupon(String couponType) {
try {
URL url = new URL(URL_STR);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 设置请求属性
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36");
conn.setRequestProperty("Accept", "*/*");
conn.setDoOutput(true);
// 构建请求体
String requestBody = String.format(
"{\"plasSsn\": \"%s\", \"userInfo\": \"%s\", \"category\": \"%s\"}",
PHONE_PLAT_SSN, USER_INFO, couponType
);
// 发送请求
try (OutputStream os = conn.getOutputStream()) {
byte[] input = requestBody.getBytes("utf-8");
os.write(input, 0, input.length);
}
// 获取响应
int responseCode = conn.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(conn.getInputStream(), "utf-8"))) {
StringBuilder response = new StringBuilder();
String responseLine;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.printf("[%s] 抢券成功! 响应: %s%n", couponType, response);
}
} else {
System.out.printf("[%s] 请求失败! 状态码: %d%n", couponType, responseCode);
}
conn.disconnect();
} catch (Exception e) {
System.out.printf("[%s] 请求异常: %s%n", couponType, e.getMessage());
}
}
}
```
### 关键改进说明:
1. **主类重命名**:
- 类名已改为`QiangPiao`,符合要求
2. **HTTP请求实现**:
- 使用`HttpURLConnection`替代OkHttp实现网络请求
- 完整的POST请求流程:
```java
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
// 设置请求头...
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
os.write(requestBody.getBytes("utf-8"));
}
```
3. **多券种并发抢购**:
- 使用线程池同时处理三种券:
```java
private static final String[] COUPON_TYPES = {
"WATER_PURIFIER",
"DISHWASHER",
"MICROWAVE_OVEN"
};
for (String couponType : COUPON_TYPES) {
executor.execute(() -> rushCoupon(couponType));
}
```
4. **定时抢购机制**:
- 北京时间2025年7月17日9:59:00开始抢购:
```java
ZonedDateTime targetTime = LocalDateTime.of(2025, 7, 17, 9, 59, 0)
.atZone(ZoneId.of("Asia/Shanghai"));
```
- 8点启动程序,等待目标时间:
```java
if (!isStarted.get() && System.currentTimeMillis() >= startTimeMillis) {
isStarted.set(true);
System.out.println("\n===== 抢购开始! =====");
}
```
5. **高频请求控制**:
- 每10ms执行一次抢购任务:
```java
executor.scheduleAtFixedRate(/* ... */, 0, 10, TimeUnit.MILLISECONDS);
```
6. **单文件运行**:
- 所有功能集成在单个Java文件中
- 无外部依赖,直接使用JDK内置库
### 使用说明:
1. 将代码保存为`QiangPiao.java`
2. 编译运行:
```bash
javac QiangPiao.java
java QiangPiao
```
3. 程序将在后台等待,直到2025-07-17 09:59:00自动开始抢券
4. 控制台会实时输出抢券结果
### 注意事项:
1. **请求频率**:每10ms发起3次请求(每种券各1次),相当于300次请求/秒,可能导致IP被封禁
2. **网络延迟**:实际抢购时间受网络延迟影响
3. **服务器限制**:需确保USER_INFO和PHONE_PLAT_SSN参数有效
4. **资源消耗**:高频请求可能消耗较多网络和CPU资源
阅读全文
相关推荐


















