时间: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(); } }