(USAR SYNCHRONIZED?)The process cannot access the file because

16 respostas
W

Oi gente, estou com o seguinte problema:
Tenho um método de gravação de log que é acessado por um versionador. De vez em quando surge a seguinte mensagem de erro no console do versionador:

C:\Versionador\Logs\nomeDoArquivo.log(The process cannot access the file because it is being used by another process)

Isso provavelmente acontece quando outro usuário de outra console faz um procedimento que acessa esse mesmo arquivo de log e coincide de estar no meio de uma gravação.
Gostaria de saber se tornando esse método sincronizado resolveria o problema, pois sendo sincronizado, o método seria acessado somente após quem o estiver acessando terminar a execução.

Mas, quando outro processo tentar acessá-lo, ele ficará aguardando o término da execução para ser liberado o acesso ao método sincronizado?
Eu sei que poderia fazer os testes, mas gostaria da opinião dos mais experientes em Java e se essa é a melhor solução para esse caso.

Segue o método de gravação.

private int GravaLog(Date data, String data) throws Exception{ int retorno = 0; try { File install = new File("C:\Versionador\Logs\nomeDoArquivo" + data + ".log"); BufferedWriter installData = new BufferedWriter(new FileWriter(install,true)); installData.write("\r\n"); installData.write("Dados a serem gravados"); installData.write("Dados a serem gravados"); installData.close(); } catch(Exception err) { retorno = 0; System.err.println(err.getMessage()); } return retorno; }

Agradeço a ajuda.

EDIT (moderador) Diminuí seu título para evitar que, ao efetuar um “reply”, as pessoas encontrem um bug no JForum que é relacionado a posts com título muito grande.

16 Respostas

Lavieri

acredito que o melhor seria… tornar o objeto File que guarda o arquivo estatico…

sempre que for usa-lo … antes de abrir o file, para inciar grevação de um SYNCH no File… assim ninguem mais vai poder abri-lo ate o termino da operação… após gravar… vc da close e sai do SYNCH … assim ja libera o File novamente…

acredito que isso resolva

T

Problemas de acesso a arquivos não podem ser resolvidos com synchronized, apenas com modos de compartilhamento de arquivos, que infelizmente o Java não implementa. Se o Java tivesse algo semelhante ao _fsopen do C++ você não teria esse tipo de problemas.

O que você pode tentar fazer é ver algumas classes de java.nio.channel.locks (se não me engano) e ver se é possível fazer alguma coisa com elas. Acho que elas só servem para travar arquivos, não para compartilhá-los (que é o seu caso).

W

Lavieri, não entendi essa parte, vc pode esclarecer melhor? Obrigado.

Não conheço essas classes, darei uma olhada. vlw.

Lavieri

Lavieri, não entendi essa parte, vc pode esclarecer melhor? Obrigado.

Não conheço essas classes, darei uma olhada. vlw.

assim

public void logErro(Throwable e) { shynchronized(Utilitario.logFile) { //... arqui vc abriria o LogFile... salvaria os dados, e fecharia o LogFile } }

mais é aquilo que o thingol comentou… não funciona bem… so serveria se todos os usuarios que acessam o Utilitario.logFile estivessem usando um mesmo servidor… caso contrario, não funcionaria

W

Cara eu tentei fazer o seguinte:

private int GravaLog(Date data, String data) throws Exception{ int retorno = 0; RandomAccessFile raf = new RandomAccessFile("C:\Versionador\Logs\nomeDoArquivo" + data + ".log", "rw"); FileChannel channel = raf.getChannel(); System.err.println("Trying lock!!!"); FileLock lock = channel.lock(); try { File install = new File("C:\Versionador\Logs\nomeDoArquivo" + data + ".log"); BufferedWriter installData = new BufferedWriter(new FileWriter(install,true)); installData.write("\r\n"); installData.write("Dados a serem gravados"); installData.write("Dados a serem gravados"); for(int i = 0; i < 100; i++){ System.out.println(i + "seg."); Thread.sleep(1000); } installData.close(); } catch(Exception err) { retorno = 0; System.err.println(err.getMessage()); } finally{ lock.release(); System.err.println("File unloked!!!"); } return retorno; }

Executei um processo da minha máquina, que acessa esse método de gravação de logs e de outra máquina executei outro processo que chama esse mesmo método. Então tenho a mensagem na console da ferramenta:

The process cannot access the file because another process has locked a portion of the file .
File unloked!!! . (mensagem que coloquei no código)
The process cannot access the file because it is being used by another process.

Continua o mesmo problema. Eu preciso que quando outro processo acessar o arquivo de log que está sendo usado fique aguardando este arquivo ser liberado pelo método e então continuar a execução.
Teria outra idéia? obrigado.

Lavieri

tenho uma ideia… da uma pesquisa em Threads … tenta fazer uma thread, que tenta acessar, se não conseguir vai pra wait() por um tempo… depois volta a tentar acessar… o ideal é que essa Thread acesse uma lista com o LOG em memoria, assim quando ele conseguir acessar o arquivo, ele ja imprime os Logs que estiverem nessa lista, que pode ser um Pilha, que vc vai esvaziando quando salva o log

T

Acho que é isso mesmo, mas como tryLock não fica esperando o lock ser liberado, provavelmente você vai ter que ficar esperando até que seja liberado. Se isso for um arquivo de log, então você teria de ter uma fila para poder ir logando em memória nessa fila enquanto o cara que está olhando o log não acabou de olhá-lo. (O problema é se o dito cujo for almoçar enquanto deixa a ferramenta de visualização do log ligada :frowning: )

Lavieri

tem 1 solução tosca pra hora de visualizar o log…

como é so leitura… (sei q vai duplicar a parada… ) mais poderia fazer algo como

copiar o log para um outro arquivo na hora de visualizar, algum arquivo que tenha a ver com o usuario, ai quando for abrir o log para leitura, não vai ficar deixando travado o arquivo de log …

talvez abrir o arquivo como leitura não trave o log

W

Vocês mencionaram ferramenta de visualização de log ou que o log pode estar sendo lido por outra pessoa. Na verdade, isso não ocorre, o arquivo de log é salvo no servidor e só fica aberto enquanto o próprio método de gravar está sendo executado. Esse arquivo só seria acessível para leitura fisicamente no servidor, o que não pode ser feito por quem executa os processos que gravam o log.
Será que essas informações facilitam?

Lavieri

na verdade isso so fara diferença na hora de leitura mesmo… mas se quase nunca houver a leitura, não há problemas…

tente fazer o Threat que fica tentando acessar o log, para poder gravar…

T

Ah, agora entendi. Há N processos que tentam gravar no mesmo arquivo; otimisticamente, não há ninguém tentando ler dele.

Se esse é o caso, você pode estar tentando efetuar um append em um arquivo em que outro processo também queira fazer um append; como você deve ter previsto, apenas um dos processos deve “ganhar” para poder gravar.
Aí o modo de compartilhamento de arquivo não é necessário, já que é necessário acesso exclusivo ao arquivo para poder escrever senão ele vai ficar todo bagunçado.
Acredito que nesse caso, se você tomar uma exceção ao tentar abrir o arquivo para gravação, seja necessário esperar um pouco (Thread.sleep com um tempo aleatório) e tentar gravar novamente. Como a espera pode atrasar seu sistema, ainda acho necessário ter uma pequena fila onde você possa pôr as últimas mensagens que ainda não foram gravadas. Você pode usar como fila a classe java.util.concurrent.LinkedBlockingQueue (por exemplo).

W

É isso mesmo thingol, e pelo menos por enquanto nem há tantos processos acessando o arquivo, o problema acontesse de vez em quando.
Mas não entendi como implementar a fila que vc descreveu.
Estou verificando se consigo algo com Threads.
Vlw.

T

Basicamente é assim. Você tem uma thread que está encarregada de gravar no arquivo de log, e que lê dessa fila cujo nome da classe lhe passei. Se a fila estiver vazia, ela espera alguém pôr na fila; se houver alguma coisa na fila, você tenta abrir o arquivo, e se conseguir você esvazia a fila gravando os elementos no log. Se não conseguir abrir o arquivo, espere um pouco, e tente novamente.
Por outro lado, todas as threads do seu sistema que tentam gravar no log simplesmente irão pôr os dados nessa fila. OK?

W

Cara, não estou conseguindo implementar sua sugestão. Verifiquei a documentação mas não compreendi como fazer o que vc sugeriu.

Criei uma classe:
public class GravaLog implements Runnable{
		
		public GravaLog(){
			//O construtor recebe os valores que serão gravados no arquivo de log.
			}
		
		public void run() {
			LinkedBlockingQueue queue = new LinkedBlockingQueue();
			queue.-->??? O que eu coloco na fila?
			try {
				metodoGravaLog();
			}catch (Exception e) {
				e.printStackTrace();
			}	
		}
	
		public metodoGravaLog(){
		//Gravação no arquivo de log ocorre aqui.
		}
	}
T

Tá bom… vou só fazer um esboço. Testar são outros 500.

Mas a dica é: faça um pouco de análise de dados. Logo de cara você tem de ver que a fila não pode ser local à sua thread, já que ela tem de ser acessível pela thread (ok) e para os processos que querem escrever nessa fila.

A sua thread deve consumir da fila (ou seja, usar “poll”) e gravar (append) no arquivo de log.

Os processos que querem fazer log devem simplesmente escrever na fila (ou seja, usar “put”).

W

Pela estrutura do código será difícil implementar dessa forma.
A solução que eu não queria utilizar será escrever um log para cada processo e no nome do log entrará o nome do pacote referente ao processo que sempre será único.
Mas obrigado pela ajuda.

Criado 17 de fevereiro de 2009
Ultima resposta 2 de mar. de 2009
Respostas 16
Participantes 3