2015-05-05 72 views
0

我可以在此获得您的看法。是否同步两个方法分别等效于同步调用这两个方法的块?

“鉴于这种情况下:这个类是为了让用户写一系列消息,让每一个消息都被标识一个时间戳和写消息的线程的名称

public class Logger 
{ 
    private StringBuilder contents = new StringBuilder(); 

    public void log(String message) 
    { 
     contents.append(Thread.currentThread().getName()); 
     contents.append("message"); 
     contents.append("\n"); 
    } 

    public String getContents() 
    { 
     return contents.toString(); 
    } 
} 

如何我们可以确保这个类的实例可以被多个线程安全地使用吗?“

显然,正确的答案是“同步log()和getContents()”。

在我看来,这仍然不够,因为一个线程在调用getContent()方法时仍然可能拉出其他线程的消息。我错了吗?

在编写消息和最终调用getContent()之间,线程是否会失去对另一个线程的锁定?

例如在UtilityClass

Logger logger = new Logger(); 
在MyThread的

..

// Get logger instance 
Logger myLogger = utilityClass.getLogger(); 

// doSomething else, release lock on 'logger' instance 
// other thread gets the Logger from utility class, and invokes the log() method on it 

// MyThread now calls getContent(), getting the other thread's message 
myLogger.getContent() 

谢谢!

+0

定义“安全”。如果同步两个方法,那么:一个日志消息不会被添加到另一个日志消息中,'getContents'永远不会返回一半的日志消息(在最后切断),并且它不会因为并发问题而崩溃。 – immibis

+2

没有人说任何线程都不应该看到其他线程的消息。也不需要在写入数据和获取内容之间保持锁定。 – RealSkeptic

+0

不完全是因为在同步黑色情况下,您正在限制用户从同步块调用。但用户可能会同步调用这些方法,所以更好的方法是使方法同步。 – Prashant

回答

0

事实上,如果只有方法是同步的,只有方法调用将是原子的。你真的需要记录器针对某个线程吗?如果是这样,你可能想要改变你的设计,以适应这种需要,例如使用ThreadLocal 真的需要线程划分。

+0

因此,同步这两种方法不会使类线程安全正确吗? – paidedly

+0

这与给定的赋值无关,它明确收集来自多个线程的消息,并且不打算成为单个线程的日志(否则,为什么要使用线程标识符标记消息?) – RealSkeptic

+1

@ user3678484:我认为你很困惑什么是线程安全。线程安全性不是类的全部属性或非属性(例如,一个类可能有条件或无条件线程安全)。您需要为您的类确定一个线程安全策略,这在您的应用程序的上下文中是有意义的。 – scottb

0

随着您发布的代码(非同步),显然有一个点(标记如下),如果getContents被称为它会收到一个不完整的记录。显然,必须添加一些同步。

public class Logger { 

    private StringBuilder contents = new StringBuilder(); 

    public void log(String message) { 
     contents.append(Thread.currentThread().getName()); 
     // << getContents called at this point. 
     contents.append(message); 
     contents.append("\n"); 
    } 

    public String getContents() { 
     return contents.toString(); 
    } 
} 

的问题是现在很清楚 - 你必须确保在log正在执行,没有其他线程可以调用getContents,因此,很显然,这两个必须同步。

public class Logger { 

    private StringBuilder contents = new StringBuilder(); 

    public synchronized void log(String message) { 
     contents.append(Thread.currentThread().getName()); 
     // << getContents called at this point. 
     contents.append(message); 
     contents.append("\n"); 
    } 

    public synchronized String getContents() { 
     return contents.toString(); 
    } 
} 

应该保证没有部分日志条目将永远不会被getContents返回。

但是,仍然有一些漏洞可能会出现不一致(例如,一个线程看不到最近发布的消息),但没有一个会导致明显的错误,例如格式错误的日志条目。

很明显:

public void test() { 
    Logger logger = new Logger(); 

    logger.log("Hello"); 
    String contents = logger.getContents(); 
    // Contents could easily contain "Hello" and more but this would not be consider an error. 
} 

可能会导致意想不到的结果,如你好入境后多日志条目,但是这不会是一个错误。

顺便说一句:你的日志方法中没有时间戳。

0

确实StringBuilder不是线程安全的。所以你不想让多个线程调用这些方法。

但是,在方法签名中使用​​关键字时,它不仅意味着2个线程不能同时访问特定的方法。 关键字​​锁定整个对象,而不仅仅是该特定的方法。

假设两种方法都有​​关键字,如果一个线程将访问getContents方法,则其他线程将不能同时访问log方法。

PS:或者,您可以使用线程安全的StringBuffer。所以你根本不需要任何形式的锁定。