有人能告诉我同步方法比同步块的优势与一个例子吗?
当前回答
谁能告诉我同步方法比同步块的优势与一个例子?谢谢。
与块相比,使用同步方法并没有明显的优势。
也许唯一的一点(但我不认为这是优点)是您不需要包含对象引用this。
方法:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
布洛克:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
看到了吗?一点好处都没有。
块确实比方法有优势,主要是灵活性,因为你可以使用另一个对象作为锁,而同步方法将锁定整个对象。
比较:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
vs.
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
另外,如果方法增长了,你仍然可以保持同步段的分离:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
其他回答
来自Java规范摘要: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
The synchronized statement (§14.17) computes a reference to an object; it then attempts to perform a lock action on that object and does not proceed further until the lock action has successfully completed. ... A synchronized method (§8.4.3.5) automatically performs a lock action when it is invoked; its body is not executed until the lock action has successfully completed. If the method is an instance method, it locks the lock associated with the instance for which it was invoked (that is, the object that will be known as this during execution of the body of the method). If the method is static, it locks the lock associated with the Class object that represents the class in which the method is defined. ...
基于这些描述,我想说以前的大多数答案都是正确的,同步方法可能对静态方法特别有用,否则您必须弄清楚如何获得“表示定义方法的类的Class对象”。
编辑:我原本以为这些是对实际Java规范的引用。澄清一下,本页只是对规范的总结/解释
主要的区别是,如果你使用同步块,你可以锁定一个对象,而不是这个,这允许更灵活。
假设您有一个消息队列和多个消息生产者和消费者。我们不希望生产者相互干扰,但是消费者应该能够检索消息,而不必等待生产者。 我们只需要创建一个对象
Object writeLock = new Object();
从现在开始,每当制作人想要添加一条新信息时,我们就会锁定它:
synchronized(writeLock){
// do something
}
因此,消费者可能仍会阅读,而生产者将被锁定。
唯一的区别是:同步块允许颗粒状锁定,不像同步方法
基本上同步块或方法被用来编写线程安全的代码,以避免内存不一致的错误。
这个问题很老了,在过去的7年里,很多事情都发生了变化。 为了线程安全,引入了新的编程结构。
您可以通过使用高级并发API而不是同步块来实现线程安全。该文档页提供了实现线程安全的良好编程结构。
锁对象支持简化许多并发应用程序的锁定习惯用法。
executor为启动和管理线程定义了高级API。concurrent提供的执行器实现提供了适合大型应用程序的线程池管理。
并发集合使管理大型数据集合变得更容易,并且可以大大减少同步的需要。
原子变量具有最小化同步和帮助避免内存一致性错误的特性。
ThreadLocalRandom(在JDK 7中)提供了从多个线程有效生成伪随机数的功能。
更好的synchronized替代品是ReentrantLock,它使用Lock API
一个可重入互斥锁,其基本行为和语义与使用同步方法和语句访问的隐式监视锁相同,但具有扩展功能。
锁的例子:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
其他编程结构也可以参考java.util.concurrent和java.util.concurrent.atomic包。
参考这个相关的问题:
同步vs锁定
TLDR;不要使用synchronized修饰符或synchronized(this){…}表达式but synchronized(myLock){…其中myLock是一个持有私有对象的最终实例字段。
在方法声明中使用synchronized修饰符与在方法主体中使用synchronized(..){}表达式的区别如下:
The synchronized modifier specified on the method's signature is visible in the generated JavaDoc, is programmatically determinable via reflection when testing a method's modifier for Modifier.SYNCHRONIZED, requires less typing and indention compared to synchronized(this) { .... }, and (depending on your IDE) is visible in the class outline and code completion, uses the this object as lock when declared on non-static method or the enclosing class when declared on a static method. The synchronized(...){...} expression allows you to only synchronize the execution of parts of a method's body, to be used within a constructor or a (static) initialization block, to choose the lock object which controls the synchronized access.
然而,使用synchronized修饰符或synchronized(…){…}使用this作为锁对象(如synchronized(this){…}),也有同样的缺点。两者都使用它自己的实例作为锁对象进行同步。这是很危险的,因为不仅对象本身,而且任何其他持有该对象引用的外部对象/代码也可以将其用作同步锁,这可能会产生严重的副作用(性能下降和死锁)。
因此,最佳实践是既不使用synchronized修饰符,也不使用synchronized(…)表达式作为锁对象,而是使用该对象的私有锁对象。例如:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
您也可以使用多个锁对象,但是需要特别注意,以确保在嵌套使用时不会导致死锁。
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
关于使用同步块的重要提示:小心你使用的锁对象!
上面user2277816的代码片段说明了这一点,将字符串字面值的引用用作锁定对象。 意识到字符串字面值在Java中是自动互缩的,您应该开始看到问题所在:在字面值“锁”上同步的每段代码都共享同一个锁!这很容易导致完全不相关的代码段发生死锁。
您需要注意的不仅仅是String对象。装箱的原语也是一种危险,因为autoboxing和valueOf方法可以重用相同的对象,这取决于值。
有关更多信息,请参阅: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused
推荐文章
- 到底是什么导致了堆栈溢出错误?
- 为什么Android工作室说“等待调试器”如果我不调试?
- Java:路径vs文件
- ExecutorService,如何等待所有任务完成
- Maven依赖Servlet 3.0 API?
- 如何在IntelliJ IDEA中添加目录到应用程序运行概要文件中的类路径?
- getter和setter是糟糕的设计吗?相互矛盾的建议
- Android room persistent: AppDatabase_Impl不存在
- Java的String[]在Kotlin中等价于什么?
- Intellij IDEA上的System.out.println()快捷方式
- 使用Spring RestTemplate获取JSON对象列表
- Spring JPA选择特定的列
- URLEncoder不能翻译空格字符
- Java中的super()
- 如何转换JSON字符串映射<字符串,字符串>与杰克逊JSON