有什么理由不在任何地方打"同步"关键字吗?

Luc*_*cky 12 java multithreading synchronization

在我的java项目中,我写的几乎所有非静态方法都是synchronized.我今天决定通过删除大多数synchronized关键字来修复一些代码.就在那里,我创建了几个线程问题,需要花费很长时间来修复,而不会增加性能.最后我恢复了一切.

我没有看到其他人在任何地方用" synchronized" 编写代码.那么,有没有任何理由,我不应该有" synchronized"无处不在?

如果我不太关心性能(即,该方法每隔几秒不被调用一次)怎么办?

akf*_*akf 36

如果不加选择地同步,则还存在创建死锁的风险.

假设我有两个类,Foo并且Bar都有一个synchronized方法doSomething().进一步假设每个类都有一个synchronized方法,将另一个类的实例作为参数.

public class Foo {

    synchronized void doSomething() {
        //code to doSomething
    }

    synchronized void doSomethingWithBar(Bar b) {
        b.doSomething();
    }

}

public class Bar {

    synchronized void doSomething() {
        //code to doSomething
    }

    synchronized void doSomethingWithFoo(Foo f) {
        f.doSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以看到,如果你有一个实例Foo和一个实例Bar,两者都试图同时doSomethingWith*在彼此之间执行它们的方法,就会发生死锁.

要强制死锁,可以在两个doSomethingWith*方法中插入一个sleep (使用Foo示例):

synchronized void doSomethingWithBar(Bar b) {
    try {
        Thread.sleep(10000);
    } catch (InterruptedException ie) {
        ie.printStackTrace();
    }

    b.doSomething();
}
Run Code Online (Sandbox Code Playgroud)

在main方法中,您启动两个线程来完成示例:

public static void main(String[] args) {
    final Foo f = new Foo();
    final Bar b = new Bar();
    new Thread(new Runnable() {
        public void run() {
            f.doSomethingWithBar(b);
        }
    }).start();

    new Thread(new Runnable() {
        public void run() {
            b.doSomethingWithFoo(f);
        }
    }).start();
}
Run Code Online (Sandbox Code Playgroud)

  • 即使没有死锁,你仍然会遇到大麻烦.我刚刚被同步块内的I/O操作击中,每周调用数千次,大约需要20毫秒.没问题.但是出于某种奇怪的原因,每周几次需要30分钟.哎哟! (4认同)

duf*_*ymo 27

当然 - 表现.监视器需要付费.

答案既不是随机删除也不是以随机方式添加同步.最好阅读Brian Goetz的"Java Concurrency In Practice"或Doug Lea的"Java Threads"等书籍,以了解如何正确地完成它.当然,要好好学习新的并发软件包.

多线程比synchronized关键字要多得多.

  • 更大的担忧,例如人们将synchronized关键字添加到方法中而没有真正理解为什么或它做什么.我担心的. (12认同)
  • 除了同步成本之外,还有更大的担忧. (4认同)
  • 另外:同步过量的不当可能导致死锁 (4认同)

Jar*_*Par 13

如果你认为在任何地方放置"synchronized"关键字是一个很好的解决方案,即使忽略了性能,那么你也不能确切地理解发生了什么,你不应该使用它.我强烈建议在没有指导的情况下深入研究这个主题.

线程是一个非常难以掌握的主题,理解你为什么要做的事情非常重要.否则你将会遇到很多代码而不是设计的代码.这最终会让你感到痛苦.

  • 或者更好的是,不要使用线程,除非你绝对,积极地必须! (7认同)

cho*_*osh 7

表现点已经说明了.

另外,请记住,所有线程都会获得堆栈的不同副本.因此,如果方法仅使用在该方法内创建的变量,并且无法访问外部世界(例如文件句柄,套接字,数据库连接等),则不会出现线程问题.


Edd*_*die 6

比放在synchronized任何地方要好得多的是仔细推理你的类所需的不变量,然后同步足以确保那些不变量.如果过度同步,则会产生两个风险:

  1. 僵局
  2. 生活问题

死锁每个人都知道.活动问题与同步成本无关,而是与全局同步多线程应用程序中的所有内容相关的事实,然后许多线程将被阻塞等待获取监视器,因为另一个线程正在触及不相关的东西但使用相同的东西监控.

如果你想在任何地方拍一个关键字以确保安全,那么我建议使用final而不是synchronized. :) 您可以做的任何事情都final有助于提高线程安全性,并更容易推理锁定需要维护的不变量.举个例子,假设你有这个琐碎的课程:

public class SimpleClass {
    private volatile int min, max;
    private volatile Date startTime;
    // Assume there are many other class members

    public SimpleClass(final int min, final int max) {
        this.min = min;
        this.max = max;
    }
    public void setMin(final int min) {
        // set min and verify that min <= max
    }
    public void setMax(final int max) {
        // set max and verify that min <= max
    }
    public Date getStartTime() {
        return startTime;
    }
}
Run Code Online (Sandbox Code Playgroud)

使用上面的类,设置min或max时,需要同步.上面的代码被破坏了.这门课有什么不变量?您需要min <= max始终确保即使多个线程正在调用setMinsetMax.

假设这是一个包含许多变量的大类并且您同步所有内容,那么如果一个线程调用setMin并且另一个线程调用getStartTime,则第二个线程将被不必要地阻塞直到setMin返回.如果你用一个有很多成员的类来做这个,并且假设只有少数成员参与必须保护的不变量,那么同步所有内容将导致这种类型的许多活跃问题.