MST

星途 面试题库

面试题:Java输入输出流配置优化时如何处理高并发下的资源竞争

假设你正在处理一个高并发的Java应用,其中多个线程需要同时读取和写入文件,在进行输入输出流配置优化时,你会采取哪些措施来避免资源竞争,确保数据的一致性和程序的高效运行,请举例说明相关的代码实现思路。
48.7万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 使用线程安全的I/O类
    • 思路:Java提供了一些线程安全的I/O类,例如RandomAccessFile在多线程环境下相对安全。它可以通过文件指针来随机访问文件,在一定程度上减少资源竞争。
    • 代码示例
import java.io.IOException;
import java.io.RandomAccessFile;

public class ThreadSafeFileAccess {
    private static final String FILE_NAME = "test.txt";
    public static void main(String[] args) {
        Thread writerThread = new Thread(() -> {
            try (RandomAccessFile raf = new RandomAccessFile(FILE_NAME, "rw")) {
                raf.seek(0);
                raf.writeUTF("Some data written by writer thread");
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        Thread readerThread = new Thread(() -> {
            try (RandomAccessFile raf = new RandomAccessFile(FILE_NAME, "r")) {
                raf.seek(0);
                String data = raf.readUTF();
                System.out.println("Data read by reader thread: " + data);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        writerThread.start();
        readerThread.start();
        try {
            writerThread.join();
            readerThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 使用同步块或锁
    • 思路:对文件的读写操作使用synchronized关键字或者Lock接口来同步,确保同一时间只有一个线程可以访问文件。
    • 代码示例
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class SynchronizedFileAccess {
    private static final String FILE_NAME = "test.txt";
    private static final Object lock = new Object();

    public static void main(String[] args) {
        Thread writerThread = new Thread(() -> {
            synchronized (lock) {
                try (PrintWriter out = new PrintWriter(new FileWriter(FILE_NAME, true))) {
                    out.println("Data written by writer thread");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread readerThread = new Thread(() -> {
            synchronized (lock) {
                try (java.io.BufferedReader in = new java.io.BufferedReader(new java.io.FileReader(FILE_NAME))) {
                    String line;
                    while ((line = in.readLine()) != null) {
                        System.out.println("Data read by reader thread: " + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        writerThread.start();
        readerThread.start();
        try {
            writerThread.join();
            readerThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 使用FileChannelByteBuffer
    • 思路FileChannel提供了更细粒度的控制和更好的性能,并且ByteBuffer可以提高数据读写的效率。通过使用FileChannellock方法可以实现文件锁机制,避免资源竞争。
    • 代码示例
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class FileChannelAndByteBufferExample {
    private static final String FILE_NAME = "test.txt";
    public static void main(String[] args) {
        Thread writerThread = new Thread(() -> {
            try (FileOutputStream fos = new FileOutputStream(FILE_NAME);
                 FileChannel channel = fos.getChannel()) {
                FileChannel lock = channel.lock();
                ByteBuffer buffer = ByteBuffer.wrap("Data written by writer thread".getBytes());
                channel.write(buffer);
                lock.release();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        Thread readerThread = new Thread(() -> {
            try (java.io.FileInputStream fis = new java.io.FileInputStream(FILE_NAME);
                 FileChannel channel = fis.getChannel()) {
                FileChannel lock = channel.lock();
                ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());
                channel.read(buffer);
                buffer.flip();
                byte[] data = new byte[buffer.limit()];
                buffer.get(data);
                System.out.println("Data read by reader thread: " + new String(data));
                lock.release();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        writerThread.start();
        readerThread.start();
        try {
            writerThread.join();
            readerThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  1. 使用线程池和队列
    • 思路:将文件读写任务提交到线程池,并通过队列来缓冲任务,避免多个线程直接竞争文件资源。这样可以更好地控制并发度,提高程序的稳定性和效率。
    • 代码示例
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolAndQueueExample {
    private static final String FILE_NAME = "test.txt";
    private static final BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<>();
    private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,  // corePoolSize
            4,  // maximumPoolSize
            10, TimeUnit.SECONDS,
            taskQueue
    );

    public static void main(String[] args) {
        Runnable writeTask = () -> {
            try (PrintWriter out = new PrintWriter(new FileWriter(FILE_NAME, true))) {
                out.println("Data written by task in thread pool");
            } catch (IOException e) {
                e.printStackTrace();
            }
        };

        executor.submit(writeTask);
        executor.submit(writeTask);

        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
                if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                    System.err.println("Pool did not terminate");
                }
            }
        } catch (InterruptedException ie) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}