Java 任何不打';同步';关键词无处不在?
在我的java项目中,我编写的几乎所有非静态方法都是同步的。我决定今天修复一些代码,删除大部分Java 任何不打';同步';关键词无处不在?,java,multithreading,synchronization,Java,Multithreading,Synchronization,在我的java项目中,我编写的几乎所有非静态方法都是同步的。我决定今天修复一些代码,删除大部分synchronized关键字。就在那里,我创建了几个线程问题,这些问题花了相当长的时间才解决,性能没有提高。最后我恢复了一切 我没有看到任何人在编写代码时到处都是“同步的”。那么有什么理由我不应该让“同步””无处不在 如果我不太关心性能(即,该方法不会每隔几秒钟调用一次以上),该怎么办?只是它几乎肯定会减慢速度 每个同步对象都必须执行某种检查,以确保它尚未被使用,然后在使用时设置标志,并在完成时重置标
synchronized
关键字。就在那里,我创建了几个线程问题,这些问题花了相当长的时间才解决,性能没有提高。最后我恢复了一切
我没有看到任何人在编写代码时到处都是“同步的”
。那么有什么理由我不应该让“同步”
”无处不在
如果我不太关心性能(即,该方法不会每隔几秒钟调用一次以上),该怎么办?只是它几乎肯定会减慢速度 每个同步对象都必须执行某种检查,以确保它尚未被使用,然后在使用时设置标志,并在完成时重置标志
这需要时间。您可能不会注意到功能的变化,但是,如果性能很重要,您需要注意。当然-性能。监视器是有成本的 答案不是以随机方式删除或添加同步。最好阅读Brian Goetz的《实践中的Java并发性》或Doug Lea的《Java线程》之类的书,以了解如何正确地实现它。当然,还要好好学习新的并发包
多线程不仅仅是synchronized关键字。性能点已经说明
另外,请记住,所有线程都会获得堆栈的不同副本。因此,如果一个方法只使用在该方法内部创建的变量,并且无法访问外部世界(例如,文件句柄、套接字、数据库连接等),那么就不可能出现线程问题 如果您不加选择地同步,您也有可能创建一个 假设我有两个类,
Foo
和Bar
,它们都有一个同步方法doSomething()
。进一步假设每个类都有一个同步方法,将另一个类的实例作为参数
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();
}
}
您可以看到,如果您有一个Foo
实例和一个Bar
实例,这两个实例都试图用*方法同时执行它们的dosomething,则可能会发生死锁
要强制死锁,可以在doSomethingWith*
方法中插入睡眠(以Foo
为例):
在main方法中,启动两个线程以完成示例:
synchronized void doSomethingWithBar(Bar b) {
try {
Thread.sleep(10000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
b.doSomething();
}
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();
}
如果您认为将“synchronized”关键字放在任何地方都是一个很好的解决方案,甚至忽略了性能,那么您就不知道到底发生了什么,不应该使用它。我强烈建议在没有方向性地开始讨论之前先仔细阅读这个话题
线程是一个非常难掌握的主题,理解为什么要做这些事情非常重要。否则,您将得到许多代码,这些代码都是偶然产生的,而不是设计出来的。这只会给你带来痛苦 比将synchronized
放在任何地方都要好得多的是仔细考虑类所需的不变量,然后进行足够的同步以确保这些不变量。如果过度同步,则会产生两种风险:
死锁
活性问题
大家都知道。活跃度问题与同步的成本无关,而是与这样一个事实有关:如果在多线程应用程序中全局同步所有内容,那么许多线程将在等待获取监视器时被阻塞,因为另一个线程正在接触不相关的内容,但使用相同的监视器
如果为了安全起见,您想在任何地方都添加关键字,那么我建议使用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;
}
}
公共类SimpleClass{
私有易失性最小值,最大值;
私有可变日期起始时间;
//假设还有许多其他的类成员
公共SimpleClass(最终整数最小值,最终整数最大值){
this.min=min;
this.max=max;
}
公共无效设置最小值(最终整数最小值){
//设置min并验证min假设您有这个示例
synchronized (lock){
doSomething();
}
如果您锁定了多个线程,则无法中断它们。最好使用锁定对象而不是“同步”,因为您可以更好地控制可能发生的中断。
当然,与所有建议一样,“synchronized”也存在一些性能问题如果您过度使用它们,可能会出现死锁、活锁等。您不应该这样做,并且您可能需要重新考虑您的设计。根据我的经验,此级别的同步无法扩展。它可能会解决您眼前的问题,但无助于修复您的整个应用程序完整性
例如:即使您有一个“已同步”的集合,一个线程可能希望一次性调用两个“已同步”方法,但不希望另一个线程看到中间结果。因此,细粒度“已同步”API的任何调用方都必须意识到这一点,这严重降低了该API的(程序员)可用性
首先,最好使用工作线程。隔离特定的长时间运行的任务,以便单独的线程接受一个不可变的输入块,并且在完成时将结果排队到主线程的消息队列中(这当然应该是同步的)。如果没有其他事情可做,主线程可能轮询或明确等待信号。具体细节取决于您的性能要求和应用程序设计
从长远来看,尝试理解数据库实现的ACID事务模型的实现