高并发文件读写

关于一个用文件做缓存的。具体过程是页面的每个请求都会触发一个线程来读取资源文件,当文件系统里不存在该文件时,则会从数据库中读取并编译出资源对象,然后序列化并保存到文件中。这样下次访问该页面时直接从文件系统中读取即可,这样达到了加速页面访问的效果。
这样的设计没什么问题,但实际实现中会有多线程同时读取同一个文件,并且某些现成读取该文件的时候其他线程可能正在写这个文件。所以这里需要进行文件同步。这里就
需要对单个文件进行锁定,所以研究了一下文件锁`FileLock`。
说到`FileLock`,就必须理解`nio`。`NIO`是最新一代的IO模型,是`BIO`的升级。`NIO`最主要的特点是:

`BIO`是基于stream的IO操作,一次操作只能读或写一个byte,这样是非常容易创建数据的filter,以及filter Chain,但是缺点是速度慢。`NIO`是基于block的IO操作,一次操作可以读或者写一个数据block,速度自然就快了。`NIO`还有个更重要的特点就是将原本`BIO`中需要进行用户空间和内科空间进行buffer拷贝操作完全编程由系统内核间的拷贝,这对性能有非常大的提升。当然随着java自身的优化,原先`BIO`的一些基于stream的api也改成基于block的实现,从而优化了部分`BIO`api的运行速度。Java`NIO`中还提供了非常有用的功能,比如`Memory-mapped file IO`以及本文的重点`File Lock`等。

文件锁实际上和普通的java对象锁类似,都是advisory锁,主要用来在一个系统的不同部分对同一个文件的读写操作进行协作。通过文件锁可以锁住整个文件,也可以锁住文件的某一部分。如果获得了独占锁,那么系统的其他地方就不能获取这个文件或者这个文件被锁住的部分。如果获取的是共享锁,那么系统其他地方仍可以获取到这个锁,并访问这个文件。通过使用文件锁,就可以达到是文件的写操作原子化,同时不会对系统的其他部分造成干扰。

下面分享下如何使用文件锁解决项目中文件同时读写的问题

1. 写线程中需要获取独占锁。

2. 读线程中不需要做任何特殊的处理。

@Slf4j
public class WriteThread implements Runnable {
File file;
int writeTime;
WriteThread(String fileName, int writeTime) {
this.file = FileUtils.getFile(fileName);
this.writeTime = writeTime;
}
@Override
public void run() {
String currentThreadName = Thread.currentThread().getName();
try {
FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();
FileLock fileLock = null;
while(true) {
try {
fileLock = fileChannel.tryLock();
break;
} catch (Exception e) {
Thread.sleep(100);
}
}
log.error("start write thread {}", currentThreadName);
fileChannel.write(ByteBuffer.wrap(("=======" + currentThreadName + "========").getBytes()));
Thread.sleep(writeTime);
log.error("end write thread {}", currentThreadName);
fileLock.release();
fileChannel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Slf4j
public class ReadThread implements Runnable {
File file;
ReadThread(String fileName) {
file = FileUtils.getFile(fileName);
}
@Override
public void run() {
String currentThreadName = Thread.currentThread().getName();
try {
FileInputStream fileInputStream = new FileInputStream(file);
log.error("start read thread name: {}", currentThreadName);
byte[] buffer = new byte[256];
StringBuilder fileContent = new StringBuilder();
int readLen = -1;
while ((readLen = fileInputStream.read(buffer)) != -1) {
fileContent.append(new String(buffer, 0, readLen));
}
log.info("read thread {} get file content: {}", currentThreadName, fileContent.toString());
log.error("end read thread name: {}", currentThreadName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果如下:

start write thread pool-1-thread-1
start read thread name: pool-1-thread-3
read thread pool-1-thread-3 get file content: =======pool-1-thread-1========
end read thread name: pool-1-thread-3
start read thread name: pool-1-thread-4
read thread pool-1-thread-4 get file content: =======pool-1-thread-1========
end read thread name: pool-1-thread-4
end write thread pool-1-thread-1
start write thread pool-1-thread-2
start read thread name: pool-1-thread-5
start read thread name: pool-1-thread-6
start read thread name: pool-1-thread-8
start read thread name: pool-1-thread-7
read thread pool-1-thread-7 get file content: =======pool-1-thread-2========
end read thread name: pool-1-thread-7
read thread pool-1-thread-5 get file content: =======pool-1-thread-2========
read thread pool-1-thread-6 get file content: =======pool-1-thread-2========
read thread pool-1-thread-8 get file content: =======pool-1-thread-2========
end read thread name: pool-1-thread-5
end read thread name: pool-1-thread-6
end read thread name: pool-1-thread-8
end write thread pool-1-thread-2

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Linux下,可以使用C++中的文件操作和多线程技术实现高并发读写串口。具体实现步骤如下: 1. 打开串口设备文件 使用C++中的文件操作函数`open()`打开串口设备文件,获取文件描述符。例如: ```c++ int fd = open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY); if (fd < 0) { perror("open serial port error!"); return -1; } ``` 其中,`/dev/ttyS0`是串口设备文件路径,`O_RDWR`表示打开文件可读可写,`O_NOCTTY`表示不把串口设备作为控制终端,`O_NDELAY`表示非阻塞模式打开。如果打开失败,使用`perror()`函数输出错误信息并返回。 2. 配置串口属性 使用Linux系统调用函数`tty_ioctl()`配置串口属性,包括波特率、数据位、停止位和校验位等。例如: ```c++ struct termios options; tcgetattr(fd, &options); cfsetispeed(&options, B9600); cfsetospeed(&options, B9600); options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cc[VTIME] = 0; options.c_cc[VMIN] = 1; tcsetattr(fd, TCSANOW, &options); ``` 其中,`cfsetispeed()`和`cfsetospeed()`函数设置输入输出波特率,`c_cflag`成员设置串口属性,`VTIME`和`VMIN`成员配置读取等待时间和最小字符数。如果配置失败,使用`perror()`函数输出错误信息并返回。 3. 创建读写线程 使用C++中的多线程技术创建读写线程,分别实现串口数据的读取和写入。例如: ```c++ void* read_thread(void* arg) { int fd = *(int*)arg; char buf[1024]; while (1) { int n = read(fd, buf, sizeof(buf)-1); if (n > 0) { buf[n] = '\0'; printf("read data: %s\n", buf); } usleep(10000); } return NULL; } void* write_thread(void* arg) { int fd = *(int*)arg; char buf[1024]; while (1) { fgets(buf, sizeof(buf), stdin); int n = write(fd, buf, strlen(buf)); if (n < 0) { perror("write serial port error!"); break; } } return NULL; } int main() { int fd = open("/dev/ttyS0", O_RDWR|O_NOCTTY|O_NDELAY); if (fd < 0) { perror("open serial port error!"); return -1; } pthread_t tid1, tid2; pthread_create(&tid1, NULL, read_thread, &fd); pthread_create(&tid2, NULL, write_thread, &fd); pthread_join(tid1, NULL); pthread_join(tid2, NULL); close(fd); return 0; } ``` 其中,`read_thread()`函数实现串口数据的读取,`write_thread()`函数实现串口数据的写入。使用`pthread_create()`函数创建两个线程,并传递串口文件描述符作为参数。主线程使用`pthread_join()`函数等待两个线程退出。如果读写失败,使用`perror()`函数输出错误信息并退出。 以上就是Linux C++高并发读写串口的实现方法。需要注意的是,在多线程环境下,需要加锁保证数据的同步和互斥访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值