第3课java_锁应用
热度🔥:33 免费课程
授课语音
Java锁的应用
1. 介绍
Java的锁机制是并发编程的重要组成部分,它确保了多个线程对共享资源的访问是互斥的,从而避免数据不一致的问题。锁可以作用于方法、代码块、对象等,锁的粒度越细,竞争越少,但复杂度也会增加。
内置锁synchronized,由JVM实现,主要分为两类:
- 对象锁:通过
synchronized(object)
修饰实例方法或代码块,确保同一时刻只有一个线程可以访问该对象的同步区域。 - 类锁:通过
synchronized(ClassName.class)
修饰静态方法或代码块,确保同一时刻只有一个线程可以访问该类的同步区域。
特点:
- 同一时间只有一个线程可以获得锁(互斥)。
- 获得锁的线程可以多次获得同一把锁(可重入)。
- 未获得锁的线程会被阻塞,直到锁被释放(阻塞)。
- 锁的获取和释放由JVM自动完成。
- 它不支持锁中断,也无法判断锁的状态,适合简单的并发场景。
显示锁Lock,由Java提供,分为:
- 公平锁:当锁释放时,先申请的线程先获取锁。
- 非公平锁:当锁释放时,后来的线程有可能先获取到锁,不按队列顺序。
特点:
- 锁的获取和释放都是手动控制,包括
lock()
和unlock()
方法。 - 支持可中断的锁请求(
lockInterruptibly()
)。 - 可以判断锁的状态(
tryLock()
)。
图示
ReentrantLock 是显示锁的实现,提供了更高级的锁控制。默认是非公平锁,支持以下操作:
tryLock()
:尝试获取锁。tryLock(long timeout, TimeUnit unit)
:支持超时机制。Condition
:实现等待通知机制。
ReentrantReadWriteLock:
- 读锁:允许多个线程同时持有,只要没有线程持有写锁。
- 写锁:独占锁,当一个线程持有写锁时,其他线程无法获得读锁或写锁,确保写的原子性。
StampedLock:
- 优化了读的场景,提供乐观读锁(CAS机制),不支持Condition,适用于读多写少的场景。
volatile修饰符:
- 是一种轻量级的同步机制,确保变量在多个线程间的可见性。
- 一个线程修改了
volatile
变量,其他线程能立即看到该修改。 volatile
变量的读写操作不会被JVM重排序,保证操作的顺序性。- 常用于单例模式的实例化、状态标识等场景。
锁的性能优化:
- CAS(Compare and Swap):乐观锁机制,是一种原子操作,避免了线程阻塞,通过不断重试实现同步。但不适合高竞争的场景。
- 自旋锁:当锁的竞争较少时,通过自旋可以减少线程挂起的开销,依赖CAS操作。
- 偏向锁:偏向第一个获取锁的线程,后续该线程不需要同步操作;适合只有一个线程访问的场景,但实现复杂,JDK 18之后已被废弃。
- 轻量级锁:无竞争情况下通过CAS获取锁;一旦有竞争,线程会自旋等待。
- 重量级锁:传统锁机制,获取失败时线程阻塞,直到锁释放。锁会有升级操作,一旦升级为重量级锁,就不能降级。
await和signal:
await
是java.util.concurrent.locks.Condition
接口的方法,线程等待直到被唤醒,必须在lock()
和unlock()
之间调用。signal
用于唤醒等待的线程,signalAll
则唤醒所有等待在condition条件上的线程。
sleep:
Thread
中的静态方法,使得线程暂停一段时间,用于延迟操作。
图示
2. 代码案例
package com.zhilitech.lock;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
public class LockExample {
// 对象锁示例
private static class ObjectLockCounter {
private int count = 0; // 计数器变量
// 增加计数器的方法,使用 synchronized 锁住整个方法(对象锁)
public synchronized void increment() {
count++; // 增加计数
}
// 获取计数器的值,使用 synchronized 锁住整个方法(对象锁)
public synchronized int getCount() {
return count; // 返回计数
}
}
// 类锁示例
private static class ClassLockCounter {
private static int count = 0; // 静态计数器变量
// 增加计数器的方法,使用 synchronized 锁住整个方法(类锁)
public static synchronized void increment() {
count++; // 增加计数
}
// 获取计数器的值,使用 synchronized 锁住整个方法(类锁)
public static synchronized int getCount() {
return count; // 返回计数
}
}
// volatile 示例
private static class VolatileCounter {
private volatile int count = 0; // volatile计数器变量
// 增加计数器的方法
public void increment() {
count++; // 增加计数
}
// 获取计数器的值
public int getCount() {
return count; // 返回计数
}
}
// ReentrantReadWriteLock 示例
private static class ReadWriteLockCounter {
private int count = 0; // 计数器变量
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); // 读写锁对象
private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock(); // 读锁
private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock(); // 写锁
// 增加计数器的方法,使用 WriteLock 锁住代码块
public void increment() {
writeLock.lock(); // 获取写锁
try {
count++; // 增加计数
} finally {
writeLock.unlock(); // 释放写锁
}
}
// 获取计数器的值,使用 ReadLock 锁住代码块
public int getCount() {
readLock.lock(); // 获取读锁
try {
return count; // 返回计数
} finally {
readLock.unlock(); // 释放读锁
}
}
}
// ReentrantLock 超时示例
private static class TimeoutLockCounter {
private int count = 0; // 计数器变量
private final ReentrantLock lock = new ReentrantLock(); // ReentrantLock对象
// 增加计数器的方法,使用 ReentrantLock 锁住代码块,并设定超时时间
public void increment() {
boolean acquired = false; // 锁获取标志
try {
acquired = lock.tryLock(100, TimeUnit.MILLISECONDS); // 尝试获取锁,设定超时
if (acquired) {
count++; // 增加计数
} else {
System.out.println("未能获取锁,跳过增加");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 中断处理
System.out.println("线程被中断");
} finally {
if (acquired) {
lock.unlock(); // 释放锁
}
}
}
// 获取计数器的值,使用 ReentrantLock 锁住代码块
public int getCount() {
lock.lock(); // 获取锁
try {
return count; // 返回计数
} finally {
lock.unlock(); // 释放锁
}
}
}
// Condition 示例 - 生产者消费者模式
private static class ProducerConsumer {
private final ReentrantLock lock = new ReentrantLock(); // ReentrantLock对象
private final Condition notEmpty = lock.newCondition(); // 不为空条件
private final Condition notFull = lock.newCondition(); // 不满条件
private final int[] buffer = new int[10]; // 缓冲区
private int count, putIndex, takeIndex; // 计数、放置索引、取出索引
// 生产者线程
public void produce(int item) throws InterruptedException {
lock.lock(); // 获取锁
try {
while (count == buffer.length) {
notFull.await(); // 缓冲区满时,等待
}
buffer[putIndex] = item; // 放入产品
putIndex = (putIndex + 1) % buffer.length; // 更新放置索引
count++; // 更新计数
notEmpty.signal(); // 唤醒消费者
} finally {
lock.unlock(); // 释放锁
}
}
// 消费者线程
public int consume() throws InterruptedException {
lock.lock(); //
获取锁
try {
while (count == 0) {
notEmpty.await(); // 缓冲区空时,等待
}
int item = buffer[takeIndex]; // 取出产品
takeIndex = (takeIndex + 1) % buffer.length; // 更新取出索引
count--; // 更新计数
notFull.signal(); // 唤醒生产者
return item; // 返回取出的产品
} finally {
lock.unlock(); // 释放锁
}
}
}
public static void main(String[] args) {
// 测试 ObjectLockCounter(对象锁)
ObjectLockCounter objectLockCounter = new ObjectLockCounter();
System.out.println("测试 ObjectLockCounter(对象锁)");
runTest(objectLockCounter);
// 测试 ClassLockCounter(类锁)
System.out.println("测试 ClassLockCounter(类锁)");
runStaticTest();
// 测试 VolatileCounter
System.out.println("测试 VolatileCounter");
VolatileCounter volatileCounter = new VolatileCounter();
runTest(volatileCounter);
// 测试 ReadWriteLockCounter(读写锁)
System.out.println("ReadWriteLockCounter(读写锁)");
ReadWriteLockCounter readWriteLockCounter = new ReadWriteLockCounter();
runTest(readWriteLockCounter);
// 测试 TimeoutLockCounter(带超时的 ReentrantLock)
System.out.println("测试 TimeoutLockCounter(带超时的 ReentrantLock)");
TimeoutLockCounter timeoutLockCounter = new TimeoutLockCounter();
runTest(timeoutLockCounter);
// 测试 ProducerConsumer(Condition 示例)
System.out.println("测试 ProducerConsumer(Condition 示例)");
ProducerConsumer producerConsumer = new ProducerConsumer();
runProducerConsumerTest(producerConsumer);
}
// 运行测试的方法
private static void runTest(Object counter) {
Runnable incrementTask = () -> {
for (int i = 0; i < 10; i++) {
if (counter instanceof ObjectLockCounter) {
((ObjectLockCounter) counter).increment(); // 增加计数
} else if (counter instanceof VolatileCounter) {
((VolatileCounter) counter).increment(); // 增加计数
} else if (counter instanceof ReadWriteLockCounter) {
((ReadWriteLockCounter) counter).increment(); // 增加计数
} else if (counter instanceof TimeoutLockCounter) {
((TimeoutLockCounter) counter).increment(); // 增加计数
}
}
};
Runnable readTask = () -> {
for (int i = 0; i < 10; i++) {
int count = 0;
if (counter instanceof ObjectLockCounter) {
count = ((ObjectLockCounter) counter).getCount(); // 获取计数
} else if (counter instanceof VolatileCounter) {
count = ((VolatileCounter) counter).getCount(); // 获取计数
} else if (counter instanceof ReadWriteLockCounter) {
count = ((ReadWriteLockCounter) counter).getCount(); // 获取计数
} else if (counter instanceof TimeoutLockCounter) {
count = ((TimeoutLockCounter) counter).getCount(); // 获取计数
}
System.out.println(Thread.currentThread().getName() + "读取Count: " + count); // 输出计数
}
};
Thread incrementThread1 = new Thread(incrementTask); // 增加计数线程1
Thread incrementThread2 = new Thread(incrementTask); // 增加计数线程2
Thread readThread1 = new Thread(readTask); // 读取计数线程1
Thread readThread2 = new Thread(readTask); // 读取计数线程2
incrementThread1.start(); // 启动增加计数线程1
incrementThread2.start(); // 启动增加计数线程2
readThread1.start(); // 启动读取计数线程1
readThread2.start(); // 启动读取计数线程2
try {
incrementThread1.join(); // 等待增加计数线程1结束
incrementThread2.join(); // 等待增加计数线程2结束
readThread1.join(); // 等待读取计数线程1结束
readThread2.join(); // 等待读取计数线程2结束
} catch (InterruptedException e) {
e.printStackTrace(); // 异常处理
}
}
// 运行类锁测试的方法
private static void runStaticTest() {
Runnable incrementTask = () -> {
for (int i = 0; i < 10; i++) {
ClassLockCounter.increment(); // 增加计数
}
};
Runnable readTask = () -> {
for (int i = 0; i < 10; i++) {
int count = ClassLockCounter.getCount(); // 获取计数
System.out.println(Thread.currentThread().getName() + "读取Count: " + count); // 输出计数
}
};
Thread incrementThread1 = new Thread(incrementTask); // 增加计数线程1
Thread incrementThread2 = new Thread(incrementTask); // 增加计数线程2
Thread readThread1 = new Thread(readTask); // 读取计数线程1
Thread readThread2 = new Thread(readTask); // 读取计数线程2
incrementThread1.start(); // 启动增加计数线程1
incrementThread2.start(); // 启动增加计数线程2
readThread1.start(); // 启动读取计数线程1
readThread2.start(); // 启动读取计数线程2
try {
incrementThread1.join(); // 等待增加计数线程1结束
incrementThread2.join(); // 等待增加计数线程2结束
readThread1.join(); // 等待读取计数线程1结束
readThread2.join(); // 等待读取计数线程2结束
} catch (InterruptedException e) {
e.printStackTrace(); // 异常处理
}
}
// 运行生产者消费者测试的方法
private static void runProducerConsumerTest(ProducerConsumer producerConsumer) {
Runnable producerTask = () -> {
for (int i = 0; i < 10; i++) {
try {
producerConsumer.produce(i); // 生产产品
System.out.println(Thread.currentThread().getName() + "生产者: " + i); // 输出生产的产品
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 中断处理
}
}
};
Runnable consumerTask = () -> {
for (int i = 0; i < 10; i++) {
try {
int item = producerConsumer.consume(); // 消费产品
System.out.println(Thread.currentThread().getName() + "消费者: " + item); // 输出消费的产品
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 中断处理
}
}
};
Thread producerThread = new Thread(producerTask); // 生产者线程
Thread consumerThread = new Thread(consumerTask); // 消费者线程
producerThread.start(); // 启动生产者线程
consumerThread.start(); // 启动消费者线程
try {
producerThread.join(); // 等待生产者线程结束
consumerThread.join(); // 等待消费者线程结束
} catch (InterruptedException e) {
e.printStackTrace(); // 异常处理
}
}
}
以上是一个完整的Java锁的应用示例,包含了各种锁的使用和相应的注释,以便理解每一部分的作用。希望这些内容能够帮助你更好地理解Java中的锁机制及其应用。