java 对文件进行加锁

在Java项目中,为了防止多个线程并发对文件进行读写导致数据不完整,通常需要进行文件加锁操作。本文介绍了如何在Java中实现文件的加锁,确保线程安全的文件读写。并提供了相关代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在公司做项目的时候,遇到了对文件的读写。在Java中,如果我们要读文件,虽然我们已经拿到了文件的引用,我们对这个文件进行了读操作,但是另外一个线程同时和可以获得这个文件的引用,对这个文件进行些操作。所以这样的结果就是我们读到的文件不是一个完整的文件,可能是一个老文件和新文件的综合体。所以我们要对文件进行加锁操作。


下面是文件操作的代码

package com.file.test;

import java.io.File;
import java.net.URI;
import java.util.Hashtable;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockFile extends File{

	private static Hashtable<String , ReentrantReadWriteLock> locks = new Hashtable<String , ReentrantReadWriteLock>();
	private ReentrantReadWriteLock lock = null;
	
	public LockFile(File arg0, String arg1) {
		super(arg0, arg1);
		lock = initLock(this.getAbsolutePath());
	}

	public LockFile(String arg0, String arg1) {
		super(arg0, arg1);
		lock = initLock(this.getAbsolutePath());
	}

	public LockFile(String arg0) {
		super(arg0);
		lock = initLock(this.getAbsolutePath());
	}

	public LockFile(URI arg0) {
		super(arg0);
		lock = initLock(this.getAbsolutePath());
	}
	
	/*
	 * 这里要注意使用 static synchronized,不然可能同时有多个进行初始化这个文件
	 */
	private static synchronized ReentrantReadWriteLock initLock(String path) {
		ReentrantReadWriteLock lock = locks.get(path);
		if (lock == null) {
			lock = new ReentrantReadWriteLock();
			locks.put(path, lock);
		}
		return lock;
	}
	
	public ReentrantReadWriteLock getLock() {
		return lock;
	}

其实上面代码很简单,对文件进行了扩展,然后使用了ReentrantReadWriteLock,对文件进行加锁。这样就能处理读写的问题了。在我们每一次读文件的时候,我们获取readlock,然后进行加锁,读后解锁。对于写的情况也是类似。


下面顺便附上测试的代码

package com.file.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class LockFileTest {
	
	private static final String FILE = "text.txt";
	
	public static void main(String args[]) {
		LockFileTest instance = new LockFileTest();
		instance.run();
	}
	
	private void run() {
		ReaderThread reader1 = new ReaderThread();
		ReaderThread reader2 = new ReaderThread();
		WriterThread writer1 = new WriterThread();
		WriterThread writer2 = new WriterThread();
		
		reader1.start();
		reader2.start();
		writer1.start();
		writer2.start();
	}
	
	class ReaderThread extends Thread {
		
		public void run() {
			
			while (true) {
				String pre = null;
				
				LockFile file = new LockFile(FILE);
				if (!file.exists()) continue;
				ReentrantReadWriteLock lock = file.getLock();
				lock.readLock().lock();
				try {
					BufferedReader reader = new BufferedReader(new FileReader(file));
					String line = reader.readLine();
					while (line != null) {
						if (pre == null) {
							pre = line;
						}
						else {
							if (!pre.equals(line)) {
								System.out.println("read wrong");
							}
						}
						line = reader.readLine();
					}
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
				lock.readLock().unlock();
			}
		}
	}
	
	class WriterThread extends Thread {
		Random random = new Random();
		public void run() {
			String s = String.valueOf(Math.abs(random.nextInt()));
			
			LockFile file = new LockFile(FILE);
			ReentrantReadWriteLock lock = file.getLock();
			lock.writeLock().lock();
			try {
				BufferedWriter writer = new BufferedWriter(new FileWriter(file));
				for (int i=0 ; i<1000 ; i++) {
						
					for (int j=0 ; j<1000 ; j++) {
						writer.write(s + System.getProperty("line.separator"));
					}
				}
				writer.flush();
				writer.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			lock.writeLock().unlock();
		}
	}
}

测试代码的逻辑也很简单。写的时候,就往一个文件里面写1000000行同样的字符,这里是使用随机函数生成的数字。读的时候,就看这个文件的1000000是不是都是一样的。这样就能测试文件的加锁是不是可以的。

明天讲写有关WeakReference的内容。

### Java FTP 文件锁定机制 在Java中实现FTP文件加锁功能可以通过多种方式完成。以下是几种常见的方法及其优缺点: #### 方法一:基于同步的文件访问控制 可以利用`Synchronized`关键字来确保同一时间只有一个线程能够操作特定的FTP文件资源[^2]。这种方式简单易懂,但在分布式环境中可能不够灵活。 ```java public class FtpFileLock { private static final Object LOCK = new Object(); public void accessFtpFile(String fileName) { synchronized (LOCK) { System.out.println(Thread.currentThread().getName() + " accessing " + fileName); // 模拟文件处理逻辑 try { Thread.sleep(1000); // 延迟模拟长时间的操作 } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(Thread.currentThread().getName() + " finished processing " + fileName); } } } ``` 这种方法适用于单机环境下的多个线程共享同一个FTP客户端实例的情况。然而,在跨机器或多节点的情况下,这种简单的同步无法满足需求。 --- #### 方法二:使用显式的锁管理器 通过引入`ReentrantLock`或其他并发工具类,可以在更细粒度上控制对FTP文件的访问权限[^2]。例如,创建一个全局锁表用于跟踪哪些文件当前被占用。 ```java import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class FtpFileLockManager { private Map<String, Lock> locks = new ConcurrentHashMap<>(); public void acquireLock(String fileName) { locks.putIfAbsent(fileName, new ReentrantLock()); Lock lock = locks.get(fileName); lock.lock(); System.out.println(Thread.currentThread().getName() + " acquired lock on " + fileName); } public void releaseLock(String fileName) { Lock lock = locks.get(fileName); if (lock != null && lock.isHeldByCurrentThread()) { lock.unlock(); System.out.println(Thread.currentThread().getName() + " released lock on " + fileName); } } } ``` 此方案适合于需要动态分配和释放锁的应用场景,并且支持可重入特性(即同一线程多次获取相同锁不会死锁)。不过需要注意的是,如果程序意外崩溃而未正常释放某些锁,则可能导致永久性的阻塞问题。 --- #### 方法三:借助外部配置库或服务协调锁状态 对于复杂的分布式系统来说,单独依靠本地内存中的数据结构难以可靠地维护一致的状态信息。此时可以选择集成专门设计用来解决这类难题的相关框架或者中间件产品[^3]。比如Redis、ZooKeeper等都可以作为可靠的分布式锁解决方案的基础组件之一;另外还有像Apache Curator这样的高级封装API可以直接提供开箱即用的功能模块供开发者调用。 下面是一个基于Curator实现基本读写互斥模式的例子: ```java import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.locks.InterProcessMutex; import org.apache.curator.retry.ExponentialBackoffRetry; public class DistributedFtpFileLock { private final String zkConnectionString = "localhost:2181"; private final CuratorFramework client = CuratorFrameworkFactory.newClient(zkConnectionString, new ExponentialBackoffRetry(1000, 3)); public DistributedFtpFileLock(){ this.client.start(); } public void lockFile(String filePath){ InterProcessMutex lock = new InterProcessMutex(client,"/" + filePath.replace("/","_")); try{ lock.acquire(); System.out.println("Locked "+filePath+" successfully."); // Perform your operation here... }catch(Exception ex){ ex.printStackTrace(); }finally{ try{if(lock!=null){lock.release();}}catch(Exception ignore){} } } } ``` 上述代码片段展示了如何利用Zookeeper配合Curator构建分布式的文件级独占型锁。它不仅解决了传统进程间通信带来的复杂性挑战,还具备良好的容错能力和扩展潜力。 --- #### 死锁预防措施 无论采用哪种技术路线图都需要警惕可能出现的死锁状况。正如之前提到过的那样当两个以上的事务相互等待对方持有的资源时就会形成循环依赖关系从而引发僵局现象[^1]。为了避免这种情况发生可以从以下几个方面入手改进设计方案: - **按顺序申请资源**: 如果所有的参与者都遵循固定的次序去请求不同的对象那么就不会存在交叉交错的现象。 - **设置超时时限**: 给每次尝试获得许可设定最大允许持续的时间一旦超过该阈值则自动放弃此次竞争机会重新排队等候下一轮轮询。 - **主动检测并解除冲突**: 定期扫描整个系统的运行情况发现潜在的风险点及时介入调整策略打破现有的束缚链条恢复正常运转流程。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值