我有一个类TestLogger
,它有一个void方法log(String s)
,可以被多个线程访问。这是我的代码
public class TestLogger {
private static final StringBuffer buffer = new StringBuffer();
public static void log(String s) {
buffer.append(s);
}
}
我不确定这里是否使用了线程安全类StringBuffer
,是否仍需要在方法log(String)
上放置synchronized关键字以确保方法的线程安全?这个方法怎么样
public static void log(String s, int type) {
if (type == 0)
buffer.append(s);
if (type == 1)
buffer.append("SOME HEADER " + s);
}
此处类型未在方法日志中修改。我是否需要使用synchronized关键字?
在Java中,同步关键字和线程安全类都可以提供线程安全性。我不确定何时使用其中一个?
答案 0 :(得分:2)
由于此方法的任一实现最多只访问buffer
一次,因此技术上不需要同步该方法,但这是一种非常糟糕的做法。
首先,它非常脆弱。一些粗心的开发人员看看这个方法并决定“优化”它(更好的Java代码!)就足以让你失去StringBuffer
的同步,这就足够了:
public static void log(String s, int type) {
if (type == 0)
buffer.append(s);
if (type == 1)
buffer.append("SOME HEADER ").optimize(s);
}
此代码段显示对append
的两次单独调用,因此如果您同时调用log('A', 1)
和log('B',1)
,则生成的缓冲区很可能是"SOME HEADER SOME HEADER AB"
。
其次,即使您在技术上没有破坏同步,仅依靠StringBuffer
来满足您的同步需求,您可能会发现轻微的行为异常。例如,考虑将来还请求记录消息的时间:
public static void log(String s) {
Date d = new Date();
buffer.append(d.toString() + " " + s);
}
如果你有大量的并发调用这个方法,你可能会遇到线程A创建新的Date
实例,上下文切换到另一个线程,完成整个方法,然后返回到线程A.这将让你的日志看起来好像它正在回溯,这绝对是不你想要的。
第三,最重要的是,将方法定义为synchronized
具有声明性值。它传达了此方法在多线程上下文中的行为方式以及您希望两个并发调用如何相互作用的信息。在旨在供他人使用的公用事业功能中,这是至关重要的。
答案 1 :(得分:2)
这个方法怎么样......
您的两种方法同样是线程安全的(或不是!,请参见下面的行)。无论调用哪种方法,都会发生同样的事情:将一个字符串添加到共享缓冲区中。
使用线程安全对象不会使程序成为线程安全的。由您决定什么是"线程安全"装置
当某人告诉你某些类是线程安全的时候,他们承诺的是,多线程调用类的方法是不可能的可以使他们中的任何一个表现在"错误"方式。
"错误"意思?这得看情况。当然,任何不同于该课程文档的行为都是错误的。通常情况下,任何与合理的程序员所期望的行为不一致的行为也可能被称为错误。
在StringBuffer的情况下,这里有什么"线程安全"意思是:它意味着:
您的示例方法调用是线程安全的,因为每个调用只能在线程安全的共享对象上进行一次调用。
如果您的示例中有多个共享对象,那么当对象的单个线程安全性不时,它们就会增加线程安全性。整个算法。
答案 2 :(得分:0)
由于StringBuffer是线程安全的,你不需要在它周围有另一个同步点(这就是synchronized会做的事情)。
答案 3 :(得分:0)
StringBuilder
在java中不是线程安全的。因此,您可以使用线程安全的StringBuffer
。
synchronized
关键字可以两种不同的方式使用。
同步方法:它使该方法成为线程安全的。
public synchronized static void log (String log) {
buffer.append(log);
}
同步语句:它使指定的对象成为线程安全的。
public static void log(String log) {
synchronized (buffer) {
buffer.append(log);
}
}