Java 在静态类中同步文件输出
我有几个线程需要写入两个不同的文本文件。到目前为止,我有以下代码:Java 在静态类中同步文件输出,java,multithreading,Java,Multithreading,我有几个线程需要写入两个不同的文本文件。到目前为止,我有以下代码: public class Logger { public static void printToGameLog(String value){ Writer writer = null; try { writer = new BufferedWriter(new OutputStreamWriter( new FileOutputSt
public class Logger {
public static void printToGameLog(String value){
Writer writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("GameLog.txt", true), "utf-8"));
synchronized(writer){
writer.write(outputString + "\r\n");
}
} catch (IOException ex){
System.out.println("cannot create log file");
}
}
public static void printToServerLog(String value){
Writer writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("serverLog.txt", true), "utf-8"));
synchronized(writer){
writer.write(outputString + "\r\n");
}
} catch (IOException ex){
System.out.println("cannot create log file");
}
}
}
这是一种可接受的方法,可以确保在同一时间不超过一个线程写入同一文件吗
如果一个线程调用其中一个方法并进入同步块,那么如果另一个线程出现并尝试执行同一个方法,会发生什么情况。当它尝试使用局部变量
writer
时,是否会尝试获取已被另一个线程锁定并因此阻塞的相同对象?我本以为它只会创建自己的独立变量,这意味着我应该将writer改为静态类变量 这是代码中的空指针异常,请尝试在静态方法上使用synchronized块
synchronized(Logger.class){
或者另一种方法是将整个方法设置为同步,如下所示
public static synchronized void printToGameLog(String value){
及
我不认为这里需要同步,只有当您的状态是从多个线程读取/写入时才需要同步 因为有单独的日志文件,我不明白为什么需要进行类级同步。似乎是一个不必要的瓶颈。我会分别为每个方法提供同步(因为它们可以同时命中不同的文件):
这是对你的问题的另一种看法。它使用一个线程来写入日志文件,只有这个线程可以访问这些文件。必须对BlockingQueue写入日志的线程:
public class ThreadedLog {
//This is some code to test the logger
public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
ThreadedLog log = new ThreadedLog("/tmp/test.txt");
// Start 100 thread that write against the log
for (int i = 0; i < 100; i++) {
new Thread(new TestLogger(log)).start();
}
}
private static class TestLogger implements Runnable {
private ThreadedLog log;
public TestLogger(ThreadedLog log) {
this.log = log;
}
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
try {
log.log("This is entry " + i + " from thread " + Thread.currentThread().getId());
} catch (InterruptedException ex) {
}
}
System.out.println(Thread.currentThread().getId() + " is done");
}
}
//________________________________________________________________________________________________
/*
* This is the code for the actual logger
*
*/
private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(10000);
private String fileName;
private Thread thread;
private Writer writer;
public ThreadedLog(String fileName) throws UnsupportedEncodingException, FileNotFoundException {
this.fileName = fileName;
thread = new Thread(new LoggingThread());
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(fileName, true), "utf-8"));
thread.start();
}
private class LoggingThread implements Runnable {
@Override
public void run() {
try {
for (;;) {
ThreadedLog.this.writer.write(queue.take() + "\r\n");
ThreadedLog.this.writer.flush();
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
try {
ThreadedLog.this.writer.close();
} catch (Exception ex) {
}
}
}
}
public void log(String string) throws InterruptedException {
queue.put(string);
}
}
公共类ThreadedLog{
//这是一些测试记录器的代码
公共静态void main(字符串[]args)引发不受支持的DencodingException、FileNotFoundException{
ThreadedLog log=newthreadedlog(“/tmp/test.txt”);
//启动100个针对日志写入的线程
对于(int i=0;i<100;i++){
新线程(新测试记录器(日志)).start();
}
}
私有静态类TestLogger实现可运行{
私有线程日志;
公共测试记录器(线程日志){
this.log=log;
}
@凌驾
公开募捐{
对于(int i=0;i<5000;i++){
试一试{
log.log(“这是来自线程“+thread.currentThread().getId())的条目“+i+”;
}捕获(中断异常例外){
}
}
System.out.println(Thread.currentThread().getId()+“完成”);
}
}
//________________________________________________________________________________________________
/*
*这是实际记录器的代码
*
*/
私有最终阻塞队列=新的ArrayBlockingQueue(10000);
私有字符串文件名;
私有线程;
私人作家;
public ThreadedLog(字符串文件名)引发不支持的DencodingException、FileNotFoundException{
this.fileName=文件名;
线程=新线程(新日志线程());
writer=new BufferedWriter(new OutputStreamWriter(
新的FileOutputStream(fileName,true),“utf-8”);
thread.start();
}
私有类LoggingThread实现Runnable{
@凌驾
公开募捐{
试一试{
对于(;;){
ThreadedLog.this.writer.write(queue.take()+“\r\n”);
ThreadedLog.this.writer.flush();
}
}捕获(中断异常| IOE异常){
e、 printStackTrace();
试一试{
ThreadedLog.this.writer.close();
}捕获(例外情况除外){
}
}
}
}
公共无效日志(字符串)引发InterruptedException{
queue.put(字符串);
}
}
如果您正在写入两个不同的文件,则无需同步。不要使用“\r\n”
——只使用“\n”
,让运行时为平台写入正确的行结尾。您的目标是协调GameLog和ServerLog的输出,还是仅仅为了防止两个线程在一条输出线内交错?我相信后者。我遇到两个线程可能同时尝试写入同一个文件的情况。谢谢你给我的提示。当然,这将给NPE。我想我会重写我的问题,使其在初始化后在编写器上同步。然而,根据您的建议,这是否意味着当一个线程正在写入游戏日志时,另一个线程可以被阻止写入服务器日志?是的,我认为这是您想要做的,我现在意识到我误解了您的建议queestion@RogerJarvis您还需要在每种情况下关闭该文件。我想这种方法的好处是线程不必花费时间等待锁释放。我想不出比其他方法有什么缺点?有一个缺点。如果您的应用程序或服务器崩溃,并且队列中仍有一些内容需要记录,则这些内容将丢失。如果你能接受这一点,这可能是一个很好的解决方案。
public class Logger
{
private static final Object GAME_LOG_LOCK = new Object();
private static final Object SERVER_LOG_LOCK = new Object();
public static void printToGameLog(String value){
synchronized (GAME_LOG_LOCK) {
Writer writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("GameLog.txt", true), "utf-8"));
writer.write(outputString + "\r\n");
} catch (IOException ex){
System.out.println("cannot create log file");
}
}
}
public static void printToServerLog(String value){
synchronized (SERVER_LOG_LOCK) {
Writer writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("serverLog.txt", true), "utf-8"));
writer.write(outputString + "\r\n");
} catch (IOException ex){
System.out.println("cannot create log file");
}
}
}
}
public class ThreadedLog {
//This is some code to test the logger
public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
ThreadedLog log = new ThreadedLog("/tmp/test.txt");
// Start 100 thread that write against the log
for (int i = 0; i < 100; i++) {
new Thread(new TestLogger(log)).start();
}
}
private static class TestLogger implements Runnable {
private ThreadedLog log;
public TestLogger(ThreadedLog log) {
this.log = log;
}
@Override
public void run() {
for (int i = 0; i < 5000; i++) {
try {
log.log("This is entry " + i + " from thread " + Thread.currentThread().getId());
} catch (InterruptedException ex) {
}
}
System.out.println(Thread.currentThread().getId() + " is done");
}
}
//________________________________________________________________________________________________
/*
* This is the code for the actual logger
*
*/
private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(10000);
private String fileName;
private Thread thread;
private Writer writer;
public ThreadedLog(String fileName) throws UnsupportedEncodingException, FileNotFoundException {
this.fileName = fileName;
thread = new Thread(new LoggingThread());
writer = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(fileName, true), "utf-8"));
thread.start();
}
private class LoggingThread implements Runnable {
@Override
public void run() {
try {
for (;;) {
ThreadedLog.this.writer.write(queue.take() + "\r\n");
ThreadedLog.this.writer.flush();
}
} catch (InterruptedException | IOException e) {
e.printStackTrace();
try {
ThreadedLog.this.writer.close();
} catch (Exception ex) {
}
}
}
}
public void log(String string) throws InterruptedException {
queue.put(string);
}
}