是否可以摆脱wait(),notify()和synchronized(obj){}并仅使用同步方法?

时间:2015-10-28 10:00:45

标签: java multithreading synchronization

我是java新手,我正在尝试理解java的concurency机制。

假设我需要使用以下指令实现任何concurency逻辑:wait(),notify(),synchronized(obj){...},synchronized方法。

是否可以在不使用wait(),notify()和synchronized(obj){...}的情况下重构代码,但只使用synchronized方法?

例如:

public class AppMe extends Thread
{ 
public Counter counter;

static class Counter
{
    protected int num = 0;

    public void increment ()
    {
        num++;
    }
}

@Override
public void run ()
{
    synchronized (counter) {
        counter.increment();
    }
}

public static void main (String[] args)
{
    AppMe thread = new AppMe();
    thread.counter = new AppMe.Counter();
    thread.start();
}
}

可以重构为:

public class AppMe extends Thread
{
public Counter counter;

static class Counter
{
    protected int num = 0;

    synchronized public void increment ()
    {
        num++;
    }
}

@Override
public void run ()
{
    counter.increment();
}

public static void main (String[] args)
{
    AppMe thread = new AppMe();
    thread.counter = new AppMe.Counter();
    thread.start();
}
}

这个例子非常简单,但它显示了在线程运行上下文中不使用wait(),notify(),synchronized(obj){...}的想法。

4 个答案:

答案 0 :(得分:2)

没有。 synchronized(obj)可以映射到synchronized方法,但没有任何内容可以向您wait()notify()购买。

答案 1 :(得分:0)

这取决于。同步是一种锁定机制,因此您可以使用同步方法获得相当远的程度(只要您了解它们的真实含义和行为)。如果你需要确保多个线程可以在一个公共对象上运行,那么普通同步就足够了。

另一方面,

wait()notify()是信号机制。如果您需要在线程和锁之间进行某种合作,则必须使用它们(实际上您应该使用更高级别的构造,但仍然如此)。这意味着代替共享一个公共对象的线程,它们实际上会根据其他线程的行为而有所不同。

答案 2 :(得分:0)

如果您使用自己的类,那么原则上您可以重构所有synchronized { }块,而不是在同步对象上调用(可能是新写入的)同步方法。但是,这是一个坏主意,因为它可能会迫使您编写几十个非常具体的方法而且可重用性很低。

按顺序调用两个同步方法也与从一个syncronized { }块一起调用它们不同。例如,给定并发map以下未正确同步,如果多个线程执行此操作,则可能抛出NPE:

if (map.containKey (x))
    map.remove (x).foo ();

通常最好使用非线程安全对象并根据需要手动同步,例如:

synchronized (map) {
    if (map.containKey (x))
        map.remove (x).foo ();
}

其中map本身并不需要线程安全。

但是,线程安全性是一个重要且有用的属性的好例子是记录器对象。

答案 3 :(得分:0)

我总是更喜欢来编写synchronized(o) { ... }而不是编写同步方法。

同步方法IMO会让您以错误的方式考虑同步,因为方法不需要同步。无论在Java程序中发生什么坏事,它的方法都不会发生任何不好的事情:只有数据才会发生坏事。

如果您想避免麻烦,请在最终私有变量引用的对象上进行同步,并使用一个名称来提醒您正在保护哪些数据。如,

private final Object payrollLock = new Object();

synchronized(payrollLock) {
    doSomethingTo(payrollThis, payrollThat, payrollTheOther);
}

Java甚至允许你将synchronized放在方法上的唯一原因是,当Java被发明时,作者认为监视器将成为银弹。解决所有并发问题。粗略地说,监视器是一个方法都是synchronized的类。 https://en.wikipedia.org/wiki/Monitor_%28synchronization%29不幸的是,虽然这种编程风格使得编写安全程序变得容易,但是编写高效的程序却很难。

我不喜欢在方法上看到synchronized的另一个原因是,因为它会诱使你在不需要时将代码行同步。

同步块总是应该尽可能小。方法应该尽可能小,但随着开发人员在这里添加一行,并在那里添加一行,它们有一种随着时间的推移而增长的方式。

在此示例中,程序员在方法中添加了一行以修复错误。新行不需要在任何锁上同步,因此程序员将其置于预先存在的synchronized语句之外。

public void frobbniz(...) {
    ...
    borkWidget.twiddleWith(numBinger, flee);  // Fixes bug 28917

    synchronized(frob_lock) {
        ...
    }
}

如果整个frobbniz()方法都是synchronized,那么即使它不需要同步,懒惰的程序员也可能只是将新行放在那里。根据twiddleWith(...)调用执行的时间长短,以及取决于frob_lock争用的其他线程,这可能会对程序产生重大的性能影响。

我不希望在方法上看到synchronized的另一个原因是因为它使锁定对象对使用您的类的人可见。只需在StackOverflow上搜索那些对在wait()/notify()对象上尝试使用Thread时发生的事情感到惊讶的人提出的所有问题,您就会明白为什么将锁定对象保密为一个好主意。