Springboot Java多线程操作本地文件,加读写锁,阻塞的线程等待运行中的线程执行完再查询并写入

Posted 蓝匣子itbluebox

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springboot Java多线程操作本地文件,加读写锁,阻塞的线程等待运行中的线程执行完再查询并写入相关的知识,希望对你有一定的参考价值。

Springboot Java多线程操作本地文件,加读写锁,阻塞的线程等待运行中的线程执行完再查询并写入

1、读写锁

在 Spring Boot 中进行多线程操作本地文件并加读写锁可以使用 Java 的 java.nio.file 包中提供的文件操作方法,以及 Java 的 java.util.concurrent 包中提供的读写锁来实现。下面是一个示例代码:

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class FileReadWriteExample 

    private static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public static void main(String[] args) 
        Path path = Paths.get("example.txt");
        Thread writer = new Thread(() -> 
            try 
                lock.writeLock().lock();
                // 写文件操作
                Files.writeString(path, "Hello World!");
             catch (Exception e) 
                e.printStackTrace();
             finally 
                lock.writeLock().unlock();
            
        );
        Thread reader = new Thread(() -> 
            try 
                lock.readLock().lock();
                // 读文件操作
                String content = Files.readString(path);
                System.out.println(content);
             catch (Exception e) 
                e.printStackTrace();
             finally 
                lock.readLock().unlock();
            
        );
        writer.start();
        try 
            writer.join();
         catch (InterruptedException e) 
            e.printStackTrace();
        
        reader.start();
    


在这个示例中,我们使用 ReentrantReadWriteLock 来实现读写锁,确保只有一个线程在执行写操作,而多个线程可以同时执行读操作。在这个示例中,我们首先创建一个 Path 对象来表示要读写的文件,然后创建一个写线程和一个读线程。写线程首先获取写锁,然后执行写操作,最后释放写锁。读线程获取读锁,执行读操作,最后释放读锁。

为了确保读线程在写线程执行完毕后再执行,我们使用 Thread.join() 方法来等待写线程执行完毕。这样,当写线程执行完毕并释放写锁时,读线程才能获取读锁并执行读操作。如果读线程在写线程还未执行完毕时尝试获取读锁,它会被阻塞,直到写线程释放写锁。

2、文件锁

在 Spring Boot 中使用 Java 多线程操作本地文件并加文件锁,阻塞的线程等待运行中的线程执行完再查询并写入可以采用以下步骤:

使用Java的java.nio.channels.FileChannel类获取文件通道。

创建一个文件锁:使用文件通道的lock()方法创建一个文件锁,以确保多线程读写文件时的互斥。

创建一个写线程:使用Java的java.lang.Thread类创建一个写线程,并在该线程中获取文件锁,然后执行写操作。

创建一个读线程:使用Java的java.lang.Thread类创建一个读线程,并在该线程中获取文件锁,然后执行读操作。

在主线程中等待写线程执行完毕:使用Java的java.lang.Thread类的join()方法,在主线程中等待写线程执行完毕。

在主线程中等待读线程执行完毕:使用Java的java.lang.Thread类的join()方法,在主线程中等待读线程执行完毕。

下面是一个示例代码:

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

public class FileReadWriteExample 

    private static final String FILE_NAME = "example.txt";

    public static void main(String[] args) 
        Thread writerThread = new Thread(() -> 
            try (RandomAccessFile file = new RandomAccessFile(FILE_NAME, "rw");
                 FileChannel channel = file.getChannel();
                 FileLock lock = channel.lock()) 
                // 执行写操作
                channel.write("Hello World!".getBytes());
             catch (IOException e) 
                e.printStackTrace();
            
        );
        Thread readerThread = new Thread(() -> 
            try (RandomAccessFile file = new RandomAccessFile(FILE_NAME, "r");
                 FileChannel channel = file.getChannel();
                 FileLock lock = channel.lock(0L, Long.MAX_VALUE, true)) 
                // 执行读操作
                String content = new String(channel.map(FileChannel.MapMode.READ_ONLY, 0L, channel.size()).array());
                System.out.println("Content: " + content);
             catch (IOException e) 
                e.printStackTrace();
            
        );

        // 启动写线程
        writerThread.start();

        try 
            // 等待写线程执行完毕
            writerThread.join();
         catch (InterruptedException e) 
            e.printStackTrace();
        

        // 启动读线程
        readerThread.start();

        try 
            // 等待读线程执行完毕
            readerThread.join();
         catch (InterruptedException e) 
            e.printStackTrace();
        
    

在这个示例中,我们使用了 FileChannel 和 FileLock 来创建文件锁,并在写线程和读线程中分别获取文件锁。
在写线程中,我们使用文件通道的 write() 方法来写入数据;在读线程中,我们使用文件通道的 map() 方法来读取数据。
同时,我们还需要在读线程中使用 lock() 方法获取共享锁,这样可以确保读线程在写线程执行完毕后再执行,避免在主线程中,

我们首先启动写线程,然后使用 join() 方法等待写线程执行完毕。接着,我们启动读线程,
并使用 join() 方法等待读线程执行完毕。这样可以确保读线程在写线程执行完毕后才会执行,从而确保读取到的数据是完整的。

需要注意的是,在使用文件锁时,需要根据具体的需求来选择锁类型。在上面的示例中,我们在写线程中使用了独占锁(lock() 方法),在读线程中使用了共享锁(lock(0L, Long.MAX_VALUE, true) 方法)
,这样可以避免写线程和读线程之间的互相干扰。

另外,需要注意文件锁的作用范围。在上面的示例中,我们只是对整个文件加了锁,如果需要对文件中的某一部分加锁,
可以使用 lock(long position, long size, boolean shared) 方法来指定锁的位置和大小。
其中,position 参数表示锁的起始位置,size 参数表示锁的大小,shared 参数表示是否为共享锁。
需要注意的是,在使用这个方法时,需要确保文件通道的当前位置在锁的起始位置上,
否则会抛出 NonWritableChannelException 或 NonReadableChannelException 异常。
可以使用 position() 方法来获取或设置文件通道的当前位置。

总之,在使用 Java 多线程操作本地文件时,加文件锁是一种保证线程安全的有效方式,
可以避免多个线程同时读写同一个文件而导致的数据不一致问题。但是需要注意锁的类型和作用范围,以及加锁的时机和释放锁的时机,以避免出现死锁等问题。

3、Synchronized和Lock的区别

synchronized 和 Lock 都是 Java 中用于实现线程同步的机制,它们的主要作用是确保多个线程对共享资源的访问顺序和互斥性,从而避免数据竞争和并发访问导致的不一致问题。

以下是 synchronized 和 Lock 的主要区别:

1、范围:synchronized 是 Java 内置的关键字,可以用于同步方法和同步代码块,而 Lock 是一个接口,需要通过实现类来使用。

2、锁的类型:synchronized 是隐式锁,即 JVM 自动为我们加锁和释放锁,而 Lock 是显式锁,需要手动加锁和释放锁。

3、性能:在 Java 5 之前,synchronized 的性能比 Lock 要好,但是在 Java 5 之后,Lock 的性能得到了大幅度提升,特别是在高并发的情况下,Lock 的性能优势更加明显。

4、可中断性:synchronized 是不可中断的锁,一旦一个线程进入了一个 synchronized 块,其他线程就必须等待该线程执行完毕并释放锁,才能继续执行。而 Lock 是可中断的锁,即一个线程在等待锁的时候,可以通过调用 lockInterruptibly() 方法来中断等待。

5、条件变量:Lock 提供了条件变量,可以使用 Condition 接口来实现等待/通知模型,而 synchronized 没有提供对应的机制。

6、灵活性:Lock 的灵活性比 synchronized 更高,例如可以选择公平锁或非公平锁,可以设置超时时间等。

总之,虽然 synchronized 和 Lock 都可以用于实现线程同步,但是它们的使用方法和特点有所不同,需要根据具体的需求来选择合适的机制。在 JDK 5 之后,由于 Lock 的性能提升和灵活性,越来越多的开发者使用 Lock 来实现线程同步。

以上是关于Springboot Java多线程操作本地文件,加读写锁,阻塞的线程等待运行中的线程执行完再查询并写入的主要内容,如果未能解决你的问题,请参考以下文章

JAVA多线程之volatile 与 synchronized 的比较

JAVA多线程之volatile 与 synchronized 的比较

Java多线程和并发总结

SpringBoot @Scheduled多线程执行

Springboot 多线程@Async

Java B2B2C多用户商城 springboot架构-config-bus(十三)