互斥锁:互斥锁是⼀种最常⻅的锁类型,⽤于实现互斥访问共享资源。在任何时刻,只有⼀个线程可以持有互斥锁,其他线程必须等待直到锁被释放。这确保了同⼀时间只有⼀个线程能够访问被保护的资源。
互斥锁的实现:
- Java提供了synchronized关键字,允许你在对象上创建一个监视器锁或锁定代码块,以确保同时只有一个线程能够执行该代码块的内容。
public class Counter {private int count = 0;// 使用synchronized关键字标记方法,确保线程安全public synchronized void increment() {count++;}// 获取当前计数值public int getCount() {return count;}
}
在上面的例子中,通过将increment方法声明为synchronized,我们确保了任何时刻都只有一个线程可以访问这个方法。这个方法是互斥的,因此可以安全地在多线程环境中更新共享资源count。
public class Counter {private final Object lock = new Object(); // 创建一个锁对象private int count = 0;public void increment() {synchronized(lock) { // 进入同步代码块,锁定lock对象count++;}}public int getCount() {return count;}
}
在这个例子中,用一个专门的私有锁对象lock来创建互斥效果。进入synchronized块时,线程会在lock对象上获得锁,退出时释放锁,从而确保count++的线程安全。
- Java的java.util.concurrent.locks包提供了一些更高级和灵活的锁实现,包括ReentrantLock。这种锁还提供了一些附加的功能和特性,如尝试锁定、定时锁定和中断等待锁的线程的能力。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private final Lock lock = new ReentrantLock(); // 创建一个ReentrantLock实例private int count = 0;public void increment() {lock.lock(); // 获取锁try {count++;} finally {lock.unlock(); // 确保释放锁}}public int getCount() {return count;}
}
⾃旋锁:⾃旋锁是⼀种基于忙等待的锁,即线程在尝试获取锁时会不断轮询,直到锁被释放。
- 在Java中,实现自旋锁可以通过循环和volatile变量来简单实现。volatile关键字确保了变量的可见性,即在一个线程中修改了该变量的值,那么在其他线程中可以立即得知这个修改。
public class SpinLock {private volatile boolean isLocked = false;public void lock() {while (isLocked) {// 循环等待,直到锁被释放// 注意:在实际应用中,这里可以添加让出CPU执行权的操作,如Thread.yield()}isLocked = true;}public void unlock() {isLocked = false;}
}
上面这个简单的自旋锁实现有一些潜在问题。最明显的是,这个锁不是公平的,也就是说,等待获取锁的线程可能会遇到饥饿现象,尤其是在高负载的情况下。另外,如果不加以控制,lock方法可能会在单核处理器系统上引起问题,因为它可能会导致永久自旋,从而阻塞其他线程的执行。
- 为了解决上述实现中的问题,提高自旋锁的效率,并且避免公平性问题,我们可以利用Java的java.util.concurrent.atomic包中提供的原子类进行改进。下面是使用AtomicBoolean实现的自旋锁示例:
import java.util.concurrent.atomic.AtomicBoolean;public class SpinLock {private AtomicBoolean lock = new AtomicBoolean(false);public void lock() {while (true) {// 尝试从false设置为true,如果成功,则获得锁if (!lock.getAndSet(true)) {return;}// 否则循环继续等待// 在实际使用中,根据需要可能会加入Thread.yield()或者Thread.sleep()减少CPU使用率}}public void unlock() {lock.set(false);}
}
通过使用AtomicBoolean的getAndSet方法,我们可以安全地检查当前锁状态,并且仅在锁未被占用时(即值为false时)将其设为true来获得锁。这个方法是原子的,即检查和设置值的操作是作为一个不可分割的整体执行的,因此可以安全使用在并发编程中。