原文: https://howtodoinjava.com/java/multi-threading/object-vs-class-level-locking/
在 Java 中,同步的代码块一次只能由一个线程执行。 另外,java 支持同时执行多个线程。 这可能会导致两个或多个线程同时访问相同的字段或对象。
同步是使所有并发线程在执行中保持同步的过程。 同步避免了由于共享内存视图不一致而导致的内存一致性错误。 当方法声明为同步时,该线程持有该方法对象的监视器或锁定对象。 如果另一个线程正在执行同步的方法,则该线程将被阻塞,直到该线程释放监视器。
请注意,我们可以在类中的已定义方法或块上使用synchronized
关键字。 synchronized
关键字不能与类定义中的变量或属性一起使用。
1. Java 中的对象级别锁定
对象级别锁定是一种机制,当我们要同步非静态方法或非静态代码块时,仅一个线程将能够执行给定类实例上的代码块。 应始终执行它,来使实例级数据线程安全。
对象级别锁定可以通过以下方式完成:
public class DemoClass
{
public synchronized void demoMethod(){}
}
or
public class DemoClass
{
public void demoMethod(){
synchronized (this)
{
//other thread safe code
}
}
}
or
public class DemoClass
{
private final Object lock = new Object();
public void demoMethod(){
synchronized (lock)
{
//other thread safe code
}
}
}
2. Java 中的类级别锁定
类级别锁防止在运行时该类的所有可用实例中的多个线程进入synchronized
块。 这意味着,如果在运行时有DemoClass
的 100 个实例,则一次只能在一个实例中的任何一个线程上执行demoMethod()
,而所有其他实例将被其他线程锁定。
应该始终执行类级别锁定以使静态数据线程安全。 我们知道 静态 关键字将方法的数据关联到类级别,因此请使用对静态字段或方法的锁定使其在类级别上。
public class DemoClass
{
//Method is static
public synchronized static void demoMethod(){
}
}
or
public class DemoClass
{
public void demoMethod()
{
//Acquire lock on .class reference
synchronized (DemoClass.class)
{
//other thread safe code
}
}
}
or
public class DemoClass
{
private final static Object lock = new Object();
public void demoMethod()
{
//Lock object is static
synchronized (lock)
{
//other thread safe code
}
}
}
3. 对象级别锁与类级别锁 – 重要说明
- Java 中的同步保证了没有两个线程可以同时或并发执行需要相同锁的同步方法。
synchronized
关键字只能与方法和代码块一起使用。 这些方法或块都可以是静态或非静态。- 每当线程进入 Java
synchronized
方法或块时,它都会获取一个锁,而当线程离开同步方法或块时,它将释放该锁。 即使线程在完成后或由于任何错误或异常而离开同步方法时,也会释放锁定。 - Java
synchronized
关键字本质上是重入者,这意味着如果同步方法调用了另一个需要相同锁的同步方法,则持有锁的当前线程可以进入该方法而无需获取锁。 - 如果在同步块中使用的对象为 null,则 Java 同步将抛出
NullPointerException
。 例如,在以上代码示例中,如果将锁初始化为 null,则“synchronized (lock)
”将抛出NullPointerException
。 - Java 中的同步方法使您的应用程序性能降低。 因此,在绝对需要时使用同步。 另外,请考虑使用同步的代码块仅同步代码的关键部分。
- 静态同步方法和非静态同步方法都可能同时或同时运行,因为它们锁定在不同的对象上。
- 根据 Java 语言规范,不能将
synchronized
关键字与构造器一起使用。 这是非法的,并导致编译错误。 - 不要在 Java 中的同步块上的非 final 字段上进行同步。 因为对非 final 字段的引用可能随时更改,然后不同的线程可能会在不同的对象上进行同步,即完全没有同步。
- 不要使用 String 字面值,因为它们可能在应用程序中的其他地方被引用,并且可能导致死锁。 使用
new
关键字创建的字符串对象可以安全使用。 但是,作为最佳实践,请在我们要保护的共享变量本身上创建一个新的私有范围内的Object
实例 OR 锁。(感谢 Anu 在评论中指出这一点。)
让我知道有关 Java 中对象级别锁与类级别锁的想法和查询。
学习愉快!