目录
一、什么是偏向锁
Java中的偏向锁是一种优化手段,用于减少没有竞争情况下的同步操作的开销。它的思想是在没有竞争情况下,将对象的标记设置为偏向,并将线程ID记录在对象头中。当有另一个线程访问该对象时,JVM会先检查对象头中记录的线程ID是否与当前线程相同,如果相同,则无需进行同步操作,直接执行即可。只有在出现竞争情况时,才会撤销偏向锁。
下面是一个简单的代码样例来说明偏向锁的使用:
public class BiasedLockDemo {
static int counter = 0;
public synchronized void increment() {
counter++;
}
public static void main(String[] args) throws InterruptedException {
BiasedLockDemo obj = new BiasedLockDemo();
// 预热
for (int i = 0; i < 10000000; i++) {
obj.increment();
}
long start = System.currentTimeMillis();
// 竞争同一个偏向锁
synchronized (obj) {
for (long i = 0; i < 1000000000L; i++) {
obj.increment();
}
}
long end = System.currentTimeMillis();
System.out.println("time: " + (end - start) + ", counter: " + counter);
}
}
在这个例子中,我们创建了一个 BiasedLockDemo
类,其中有一个 increment()
方法用于对 counter
自增操作。在 main()
方法中,我们首先对 obj
进行了一些预热操作,然后使用 synchronized
关键字竞争同一个偏向锁来进行自增操作。最后打印出时间和计数器的值。
在运行该示例时,由于只有一个线程对 counter
进行了自增操作,所以JVM会将对象标记为偏向状态,并记录线程ID。因此,在进行实际的自增操作时,JVM会直接执行,无需进行同步操作,从而提高了程序的性能。
二、什么是轻量级锁
Java中的轻量级锁是一种基于CAS操作 (Compare and Swap) 的自旋锁技术,它适用于多线程环境下对共享资源的访问控制。当一个线程尝试获取轻量级锁时,它会使用CAS操作将对象头部的Mark Word标记为“锁定”状态,并将指向当前线程栈帧的指针保存在Mark Word中,如果这个操作成功了,就代表该线程成功获取到了锁。如果锁已经被别的线程占用,则当前线程会进行自旋操作,不断尝试获取锁,直到获得锁或者自旋次数达到某个阈值。
以下是一个简单的Java代码示例,演示了如何使用synchronized关键字来使用轻量级锁:
public class Counter {
private int count = 0;
public synchronized void increment() { // 使用synchronized关键字
count++;
}
public int getCount() {
return count;
}
}
在上面的示例中,increment()方法使用了synchronized关键字,这会使锁机制自动采用轻量级锁。另外,需要注意的是,在Java 6之前的版本中,轻量级锁仅在Server VM模式下可用,如果使用的是Client模式,则依然会采用传统的重量级锁。
三、偏向锁和轻量级锁的区别
在Java中,偏向锁和轻量级锁都是为了提高多线程程序的性能而设计的。它们的区别在于锁的竞争情况不同。
偏向锁是指当一个线程获取了对象的锁之后,在接下来的一段时间内,该对象的锁会偏向于该线程,即其他线程在获取该对象锁时将不再进行竞争,直接进入可偏向状态,从而避免了不必要的锁竞争,提高了程序的性能。
轻量级锁则是指当一个线程尝试获取对象的锁时,如果该对象当前没有被锁定,则该线程会尝试使用CAS(Compare And Swap)操作来获取锁,如果成功就获得锁,如果失败就说明有其他线程竞争了锁,此时该线程就会进入自旋状态等待锁的释放或者升级为重量级锁。
因此,偏向锁适用于对锁的持有时间较短的场景,而轻量级锁适用于对锁的持有时间较长的场景。