java中的对象级别锁定 VS 类级别锁定
同步是能够将对共享资源的访问权限限制为仅一个线程。
当两个或者多个线程需要访问共享资源时,必须有一些机制,使得仅一个线程将使用共享资源。
我们可以实现它的过程称为同步。
为什么你需要同步?
让我们在举例的帮助下了解这一点。
假设我们想要计算我们为特定URL获得的请求数。
如果我们同时获得两个请求,则计数可能不一致。
没有同步:
package org.igi.theitroad; public class RequestCounter { private int count; public int incrementCount() { count++; return count; } }
例如:线程T1看到数量为20并将其递增到21.同时,线程T2也看到算作20并将其递增到21.这表明计数变得不一致。
具有同步:
我们可以使用两种方式实现同步。
- 同步方法
- 同步块
You can not use synchronized with instance or class variables.
同步方法
我们可以使整个increntmentCount()方法同步,因此没有两个线程可以并行访问它。
package org.igi.theitroad; public class RequestCounter { private int count; public synchronized int incrementCount() { count++; return count; } }
例如:线程T1看到计数为20并将其递增到21.同时,线程T2现在将看到计数为21并将其递增至22.
同步块
我们可以使用块以IncrementCount()方法中的关键部分同步关键部分,因此没有两个线程可以同时访问块。
package org.igi.theitroad; public class RequestCounter { private int count; public int incrementCount() { synchronized (this) { count++; return count; } } }
例如:线程T1看到计数为20并将其递增到21.同时,线程T2现在将看到计数为21并将其递增至22.
Java中有两种锁定。
- 对象级别锁定
- 程序锁定
对象级别锁定:
对象级别锁定意味着要同步非静态方法或者块,以便在该实例中仅由一个线程访问它。
如果要保护非静态数据,则使用它。
我们可以通过以下实现对象级别锁定。
使方法同步:
public synchronized int incrementCount() { }
使用同步块并锁定:
public int incrementCount() { synchronized (this) { count++; return count; }
使用Synchronized块并锁定其他对象:
private final Object lock=new Object(); public int incrementCount() { synchronized (lock) { count++; return count; }
Class Level Locking:
类级别锁定意味着要同步静态方法或者块,以便仅为全类类的一个线程访问它。
如果我们有10个类实例,则只有一个线程一次只能访问一个方法或者一次任何一个实例的块。
如果要保护静态数据,则使用它。
这可以通过以下方式实现:
使静态方法同步:
public static synchronized int incrementCount() { }
使用同步块并锁定.class:
public int incrementCount() { synchronized (RequestCounter.class) { count++; return count; }
使用同步块并锁定一些其他静态对象:
private final static Object lock=new Object(); public int incrementCount() { synchronized (lock) { count++; return count; }
两个线程可以同时执行静态和非静态方法吗?
是的,由于两个线程将在不同对象上获取锁定,因此可以在没有任何问题的情况下同时执行。
如果同步一个类方法,并且不同一类的其他方法不同步?它们可以由两个线程同时执行吗?
是的,因为一个线程将需要锁定进入同步块,但第二个线程将执行不需要任何锁的非同步方法,因此可以同时执行。
从另一个同步方法调用同步方法是否安全?
是的,从另一个同步方法调用同步方法是安全的,因为调用同步方法时,我们将锁定此对象,当我们调用另一个同一类的另一个同步方法时,可以安全地执行它已锁定这个对象。
例如:
public synchronized void method1() { method2(); //some code } public synchronized void method2() { //some code }
你实际上是这样做的。
public void method1() { synchronized (this) { method2(); //some code } } public void method2() { synchronized (this) { //some code } }
这里如果来自方法1的任何线程调用方法2,它已经在此对象上锁定,因此执行安全。