有人能告诉我同步方法比同步块的优势与一个例子吗?
当前回答
大多数情况下,我使用它来同步对列表或映射的访问,但我不想阻止对对象的所有方法的访问。
在下面的代码中,修改列表的线程不会阻塞等待正在修改映射的线程。如果方法在对象上是同步的,那么每个方法都必须等待,即使它们所做的修改不会冲突。
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
其他回答
在实际应用中,同步方法相对于同步块的优势在于它们更能抵抗白痴;因为您不能选择任意对象来锁定,所以您不能滥用synchronized方法语法来做一些愚蠢的事情,比如锁定字符串文字或锁定从线程下面更改的可变字段的内容。
另一方面,使用同步方法,您无法保护锁不被任何可以获得对象引用的线程获取。
因此,在方法上使用synchronized作为修饰符可以更好地保护你的奶牛免受伤害,而将synchronized块与私有final锁对象结合使用则可以更好地保护你自己的代码免受奶牛的伤害。
关于使用同步块的重要提示:小心你使用的锁对象!
上面user2277816的代码片段说明了这一点,将字符串字面值的引用用作锁定对象。 意识到字符串字面值在Java中是自动互缩的,您应该开始看到问题所在:在字面值“锁”上同步的每段代码都共享同一个锁!这很容易导致完全不相关的代码段发生死锁。
您需要注意的不仅仅是String对象。装箱的原语也是一种危险,因为autoboxing和valueOf方法可以重用相同的对象,这取决于值。
有关更多信息,请参阅: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused
注意:静态同步方法和块工作在Class对象上。
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
我想这个问题是关于线程安全单例和带有双重检查锁定的惰性初始化之间的区别。当我需要实现某些特定的单例时,我总是会参考这篇文章。
这是一个线程安全单例:
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
优点: 延迟初始化是可能的。 它是线程安全的。 缺点: getInstance()方法是同步的,因此它会导致性能变慢,因为多个线程不能同时访问它。
这是一个带有双重检查锁定的Lazy初始化:
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
优点: 延迟初始化是可能的。 它也是线程安全的。 克服了synchronized关键字导致的性能下降。 缺点: 第一次,它会影响性能。 由于双止回锁方法的缺点是可以承受的,所以可以 用于高性能多线程应用程序。
详情请参考这篇文章:
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
在同步方法的情况下,锁将在对象上获得。但是如果你使用同步块,你可以选择指定一个对象来获取锁。
例子:
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 自动化invokerrequired代码模式
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder