时间:2023-03-08 12:02:45 | 栏目:JAVA代码 | 点击:次
什么是显式锁?
由自己手动获取锁,然后手动释放的锁。
有了 synchronized(内置锁) 为什么还要 Lock(显示锁)?
使用 synchronized 关键字实现了锁功能的,使用 synchronized 关键字将会隐式地获取锁,但是它将锁的获取和释放固化了,也就是先获取再释放。
与内置加锁机制不同的是,Lock 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁的方法都是显式的。
| 方法名称 | 描述 |
|---|---|
| void lock() | 获取锁 |
| void lockInterruptibly() throws InterruptedException | 可中断的获取锁,和lock()方法的不同之处在于该方法会响应中断,即在锁的获取中可以中断当前线程 |
| boolean tryLock() | 尝试非阻塞的获取锁,调用该方法后立刻返回,如果能够获取则返回true,否则返回false |
| boolean tryLock(long time, TimeUnit unit) throws InterruptedException | 超时获取锁,当前线程会在以下三种情况下会返回: 1. 当前线程在超时时间内获得了锁 2.当前线程在超市时间内被中断 3. 超时时间结束,返回false |
| void unlock(); | 释放锁 |
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
Lock接口常用的实现类是 ReentrantLock。
示例代码:主线程100000次减,子线程10万次加。
public class ReentrantLockTest {
private Lock lock = new ReentrantLock();
private int count = 0;
public int getCount() {
return count;
}
private static class ReentrantLockThread extends Thread {
private ReentrantLockTest reentrantLockTest;
public ReentrantLockThread(ReentrantLockTest reentrantLockTest) {
this.reentrantLockTest = reentrantLockTest;
}
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
reentrantLockTest.incr();
}
System.out.println(Thread.currentThread().getName() + " end, count = " + reentrantLockTest.getCount());
}
}
private void incr() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
private void decr() {
lock.lock();
try {
count--;
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ReentrantLockTest reentrantLockTest = new ReentrantLockTest();
new ReentrantLockThread(reentrantLockTest).start();
for (int i = 0; i < 100000; i++) {
// 递减100000
reentrantLockTest.decr();
}
System.out.println(Thread.currentThread().getName() + " count = " + reentrantLockTest.getCount());
}
}
简单地讲就是:“同一个线程对于已经获得到的锁,可以多次继续申请到该锁的使用权”。而 synchronized 关键字隐式的支持重进入,比如一个 synchronized 修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁
同样,ReentrantLock 在调用 lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞
ReentrantReadWriteLock 是 ReadWriteLock 的实现类。
之前提到锁基本都是排他锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有了很大提升。
读锁不排斥读锁,但是排斥写锁;写锁即排斥读锁也排斥写锁。
private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock getLock = lock.readLock(); // 读锁 private final Lock setLock = lock.writeLock(); // 写锁
至于上锁、解锁与 ReentrantLock 使用方式一致。
常用api
| 方法名称 | 描述 |
|---|---|
| void await() throws InterruptedException | 使当前线程进入等待状态直到被通知(signal)或中断 |
| void signal() | 唤醒一个等待的线程 |
| void signalAll() | 唤醒所有等待的线程 |
示例代码,主线程调用方法唤醒两个子线程。
public class ConditionTest {
private volatile boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private void task1() {
lock.lock();
try {
try {
System.out.println(Thread.currentThread().getName() + " 等待中");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 等待结束");
System.out.println("发送邮件");
} finally {
lock.unlock();
}
}
private void task2() {
lock.lock();
try {
try {
System.out.println(Thread.currentThread().getName() + " 等待中");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 等待结束");
System.out.println("发送短信");
} finally {
lock.unlock();
}
}
private void updateFlag() {
lock.lock();
try {
this.flag = true;
this.condition.signalAll();
}finally {
lock.unlock();
}
}
private static class ConditionThread1 extends Thread {
private ConditionTest conditionTest;
public ConditionThread1(ConditionTest conditionTest) {
this.conditionTest = conditionTest;
}
@Override
public void run() {
if (!conditionTest.flag) {
conditionTest.task1();
}
}
}
private static class ConditionThread2 extends Thread {
private ConditionTest conditionTest;
public ConditionThread2(ConditionTest conditionTest) {
this.conditionTest = conditionTest;
}
@Override
public void run() {
if (!conditionTest.flag) {
conditionTest.task2();
}
}
}
public static void main(String[] args) throws InterruptedException {
ConditionTest conditionTest = new ConditionTest();
new ConditionThread1(conditionTest).start();
new ConditionThread2(conditionTest).start();
Thread.sleep(1000);
System.out.println("flag 改变。。。");
conditionTest.updateFlag();
}
}