一、高并发概述
高并发是指系统需要同时处理大量的请求或任务的能力,也就是系统的并发处理能力。在互联网应用中,高并发能够提高系统的吞吐量和资源利用率,是衡量系统性能的重要指标。
二、高并发的使用场景
互联网大型网站:如电商促销活动(双十一、黑五)、抢票系统、直播弹幕系统等,需要同时处理大量用户请求。
金融交易系统:如股票交易、在线支付,需要在短时间内处理大量交易请求,保证数据一致性。
即时通讯应用:如微信、QQ,需要实时处理大量消息的收发。
游戏服务器:在线游戏需要同时支持大量玩家的实时交互。
物联网应用:大量的设备同时向服务器发送数据,需要高并发的处理能力。
三、高并发的代码案例
- 使用 Java 的多线程处理高并发
以下是一个简单的示例,使用线程池来处理高并发任务:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class HighConcurrencyExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 模拟高并发请求
for (int i = 0; i < 1000; i++) {
final int index = i;
executorService.execute(() -> {
processRequest(index);
});
}
// 关闭线程池
executorService.shutdown();
}
public static void processRequest(int index) {
// 处理请求的逻辑
System.out.println("处理请求:" + index);
}
}
说明:
使用 ExecutorService 创建一个线程池,提高线程重用率,避免频繁创建和销毁线程的开销。
模拟了 1000 个并发请求,通过线程池来处理。
2. 使用 Netty 框架构建高并发服务器
Netty 是一款高性能的 Java NIO 网络框架,适用于开发高并发的网络应用程序。
package com.example;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
public class NettyServer {
public static void main(String[] args) throws Exception {
// 创建 Boss 和 Worker 线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// 创建服务器启动对象
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // 使用 NIO 通信方式
.childHandler(new ChannelInitializer<Channel>() { // 初始化通道
@Override
protected void initChannel(Channel ch) throws Exception {
// 添加自定义的处理器
ch.pipeline().addLast(new SimpleServerHandler());
}
});
// 绑定端口,启动服务
ChannelFuture future = bootstrap.bind(8080).sync();
System.out.println("服务器启动在端口:8080");
// 监听关闭事件
future.channel().closeFuture().sync();
} finally {
// 优雅地关闭线程组
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
// 自定义处理器
static class SimpleServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf inBuffer = (ByteBuf) msg;
String received = inBuffer.toString(io.netty.util.CharsetUtil.UTF_8);
System.out.println("服务器接收到消息:" + received);
// 回复客户端
String response = "处理成功";
ByteBuf outBuffer = Unpooled.copiedBuffer(response, io.netty.util.CharsetUtil.UTF_8);
ctx.writeAndFlush(outBuffer);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
}
说明:
使用 Netty 构建了一个简单的服务器,可以处理大量并发的客户端连接和请求。
Netty 基于 NIO 的事件驱动机制,可以有效地提升高并发处理能力。
并发锁的使用场景和代码案例
一、并发锁的概述
在高并发场景下,为了保证共享资源的正确性,避免数据不一致等问题,需要对一些关键代码块进行同步控制,并发锁就是实现线程同步的一种机制。
二、并发锁的使用场景
共享资源的互斥访问:当多个线程需要读写同一个共享变量或数据结构时,需要使用锁来保证同一时刻只有一个线程访问。
保证数据一致性:在对数据进行修改、更新操作时,确保线程之间的数据一致性。
避免死锁和资源竞争:通过合理使用锁,避免线程因为资源竞争而陷入死锁。
三、并发锁的代码案例
1. 使用 synchronized 关键字
package com.example;
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
package com.example;
public class SynchronizedExample {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// 创建多个线程同时访问 increment 方法
Thread t1 = new Thread(() -> runIncrement(counter));
Thread t2 = new Thread(() -> runIncrement(counter));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终计数值:" + counter.getCount());
}
public static void runIncrement(Counter counter) {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
}
}
说明:
increment() 方法被 synchronized 修饰,保证同一时刻只有一个线程能执行该方法,防止并发问题。
多个线程同时对 count 进行自增操作,如果不使用锁,可能导致计数结果不正确。
2. 使用 ReentrantLock
package com.example;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int count = 0;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
} finally {
lock.unlock(); // 释放锁
}
}
public int getCount() {
return count;
}
}
package com.example;
public class ReentrantLockExample {
public static void main(String[] args) throws InterruptedException {
LockExample counter = new LockExample();
// 创建多个线程同时访问 increment 方法
Thread t1 = new Thread(() -> runIncrement(counter));
Thread t2 = new Thread(() -> runIncrement(counter));
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("最终计数值:" + counter.getCount());
}
public static void runIncrement(LockExample counter) {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
}
}
说明:
使用 ReentrantLock 来实现显式锁控制,功能类似于 synchronized,但提供了更灵活的功能。
需要手动获取和释放锁,必须在 finally 块中释放锁,避免因异常导致死锁。
3. 使用 ReadWriteLock
package com.example;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockExample {
private int value = 0;
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
public void increment() {
rwLock.writeLock().lock();
try {
value++;
} finally {
rwLock.writeLock().unlock();
}
}
public int getValue() {
rwLock.readLock().lock();
try {
return value;
} finally {
rwLock.readLock().unlock();
}
}
}
说明:
ReadWriteLock 允许多个读线程同时访问,但在写操作时互斥,可以提高并发性能。
适用于读多写少的场景。
4. 使用 StampedLock
package com.example;
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private int value = 0;
private final StampedLock lock = new StampedLock();
public void increment() {
long stamp = lock.writeLock();
try {
value++;
} finally {
lock.unlockWrite(stamp);
}
}
public int getValue() {
long stamp = lock.tryOptimisticRead();
int currentValue = value;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
currentValue = value;
} finally {
lock.unlockRead(stamp);
}
}
return currentValue;
}
}
说明:
StampedLock 是一种改进的读写锁,提供了乐观读的功能,可以进一步提高并发性能。
适用于高并发读的场景,需要注意 validate 方法的使用。
总结
高并发主要应用于需要同时处理大量请求的系统,提升系统的吞吐量和响应能力。
并发锁在高并发场景下,用于保证数据的正确性和一致性,防止资源竞争和数据错乱。
常用的并发锁包括 synchronized、ReentrantLock、ReadWriteLock、StampedLock 等,根据不同的场景选择合适的锁机制。