Java并发Lock接口实现示例详解
作者:右耳菌
Locks包 类层次结构
Lock接口
方法签名 | 描述 | 说明 |
---|---|---|
void lock(); | 获取锁(不死不休) | 一直获取锁,直到拿到为止 |
boolean tryLock(); | 获取锁(浅尝辄止) | 尝试获得锁,获取不到就算了 |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; | 获取锁(过时不候) | 超时限制,超过时间就放弃 |
void lockInterruptibly() throws InterruptedException; | 获取锁(任人摆布) | 可以在外部通过方法中断 |
void unlock(); | 释放锁 | |
Condition newCondition(); |
结论:
1、lock()最常用;
2、lockInterruptibly()方法一般更昂贵,有的impl可能没有实现lockInterruptibly(),只有真的需要效应中断时,才使用,使用之前看看impl对该方法的描述。
trylock
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主线程拿到锁 Thread th = new Thread(new Runnable() { @Override public void run() { boolean rs = lock.tryLock(); System.out.println("是否获取到锁: " + rs); } }); th.start(); Thread.sleep(2000L); th.interrupt();//中断线程运行 System.out.println("th 线程被中断了"); } }
是否获取到锁: false
th 线程被中断了
- trylock带超时
package lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主线程拿到锁 Thread th = new Thread(new Runnable() { @Override public void run() { boolean rs = false; try { rs = lock.tryLock(1, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("是否获取到锁: " + rs); } }); th.start(); Thread.sleep(2000L); th.interrupt();//中断线程运行 System.out.println("th 线程被中断了"); } }
是否获取到锁: false
th 线程被中断了
lockInterruptibly
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主线程拿到锁 Thread th = new Thread(new Runnable() { @Override public void run() { try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println("获取锁时被中断了"); e.printStackTrace(); } } }); th.start(); Thread.sleep(2000L); th.interrupt();//中断线程运行 System.out.println("th 线程被中断了"); } }
th 线程被中断了
获取锁时被中断了
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at lock.GetLock_Demo$1.run(GetLock_Demo.java:16)
at java.lang.Thread.run(Thread.java:748)
lock and unlock
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主线程拿到锁 Thread th = new Thread(new Runnable() { @Override public void run() { System.out.println("尝试获得锁"); lock.lock(); System.out.println("获得锁了"); } }); th.start(); Thread.sleep(2000L); th.interrupt();//中断线程运行 System.out.println("th 线程被中断了"); Thread.sleep(5000L); lock.unlock(); } }
尝试获得锁
th 线程被中断了
获得锁了
Condition
Condition 一般是将其中的await和signal成对使用的,且一般是await在前signal在后,而且调用的使用,应该确保本身是获取到锁的情况下,不然会出现以下问题:
1. await 和 signal 方法应该在lock内部调用,否则会发生 IllegalMonitorStateException
异常
package lock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Condition_Demo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { lock.lock(); try { System.out.println("当前线程:" + Thread.currentThread().getName() + "获得锁"); condition.await(); //因为这里将线程挂起,所以后面无法执行 System.out.println("当前线程:" + Thread.currentThread().getName() + "开始执行~"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }; thread.start(); Thread.sleep(2000L); System.out.println("休眠2s,来控制线程"); condition.signal(); //直接唤醒会报错,因为lock方法执行在Thread-0线程内部,而我们代码在这里执行的是main线程,所以会报错, } }
当前线程:Thread-0获得锁
休眠2s,来控制线程
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
at lock.Condition_Demo.main(Condition_Demo.java:33)
2. signal应该在await后调用,否则会导致死锁
package lock; import sync.ReentrantLockDemo; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Condition_Demo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { try { Thread.sleep(3000L); System.out.println("休眠3秒,等待主线程先执行."); } catch (InterruptedException e) { e.printStackTrace(); } lock.lock(); try { System.out.println("当前线程:" + Thread.currentThread().getName() + "获得锁"); condition.await(); //因为这里将线程挂起,所以后面无法执行 System.out.println("当前线程:" + Thread.currentThread().getName() + "开始执行~"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }; thread.start(); Thread.sleep(2000L); System.out.println("休眠2s,来控制线程"); lock.lock(); condition.signal(); //直接唤醒会报错,因为lock方法执行在Thread-0线程内部,而我们代码在这里执行的是main线程,所以会报错, lock.unlock(); //获取到了这把锁,然后解锁. //2.当然这里会出现死锁的,如果signal方法在我们的await之前执行,那么这里就会死锁 } }
休眠2s,来控制线程
休眠3秒,等待主线程先执行.
当前线程:Thread-0获得锁
// 这里死锁了
- 使用condition实现阻塞队列的例子
package lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class BlockingQueue_Demo { public static void main(String[] args) throws InterruptedException { BlockingQueue kaneBlockingQueue = new BlockingQueue(6); new Thread() { @Override public void run() { for (int i = 0; i < 10; i++) { kaneBlockingQueue.put("x" + i); } } }.start(); Thread.sleep(1000L); System.out.println("开始取元素"); for (int i = 0; i < 8; i++) { kaneBlockingQueue.take(); Thread.sleep(2000); } } } class BlockingQueue { List<Object> list = new ArrayList<>(); private Lock lock = new ReentrantLock(); private Condition putCondition = lock.newCondition(); //condition可以有多个,针对不同的操作放入不同condition,相当于等待队列 private Condition takeCondition = lock.newCondition(); private int length; public BlockingQueue(int length) { this.length = length; } public void put(Object obj) { lock.lock(); //思考一个读一个写,为什么要加锁呢? try { while (true) { if (list.size() < length) { //我们集合的长度不能超过规定的长度,才能向里面放东西 list.add(obj); System.out.println("队列中放入元素:" + obj); takeCondition.signal(); return; } else { //如果放不进去,就该阻塞. --利用condition实现 putCondition.await();//挂起 } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public Object take() { lock.lock(); try { while (true) { if (list.size() > 0) { Object obj = list.remove(0); System.out.println("队列中取得元素:" + obj); putCondition.signal(); return obj; } else { takeCondition.await(); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); return null; } } }
队列中放入元素:x0
队列中放入元素:x1
队列中放入元素:x2
队列中放入元素:x3
队列中放入元素:x4
队列中放入元素:x5
开始取元素
队列中取得元素:x0
队列中放入元素:x6
队列中取得元素:x1
队列中放入元素:x7
队列中取得元素:x2
队列中放入元素:x8
队列中取得元素:x3
队列中放入元素:x9
队列中取得元素:x4
队列中取得元素:x5
队列中取得元素:x6
队列中取得元素:x7
Process finished with exit code 0
可重入锁 ReentrantLock
一般来说,如果可重入锁的加锁次数是n,那么解锁次数也得是n才能完全释放锁,否则,如果小于n 则无法正常释放锁,此时如果有别的线程要加锁,则无法获取到锁而被阻塞;如果大于n,则会触发 IllegalMonitorStateException
异常, ReentrantLock 默认是使用非公平锁,如果要使用公平锁,可以使用 new ReentrantLock(true)
来创建。
- 小于n的情况
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Reentrant_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) { lock.lock(); System.out.println(Thread.currentThread().getName() + "获得第1次锁"); lock.lock(); System.out.println(Thread.currentThread().getName() + "获得第2次锁"); lock.unlock(); new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始去释放锁"); lock.lock(); System.out.println("获得锁成功~~~"); lock.unlock(); } }.start(); } }
main获得第1次锁 main获得第2次锁 Thread-0开始去释放锁 // 子线程获取锁失败导致阻塞了
- 大于n的情况修改成3次unlock
// 修改成3次unlock lock.unlock(); lock.unlock(); lock.unlock();
main获得第1次锁 main获得第2次锁 Exception in thread "main" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) at lock.Reentrant_Demo.main(Reentrant_Demo.java:18)
简单说明图
实现一个ReenrantLock的demo版本 - 一个现实思想的简单版本
package lock; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.LockSupport; public class ReentrantLock_Demo implements Lock { //记录锁的拥有者 AtomicReference<Thread> owner = new AtomicReference<>(); //记录重入次数的count AtomicInteger count = new AtomicInteger(0); //等待队列 private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue(); @Override public boolean tryLock() { //判断count值是否为0,如果count不等于0,说明锁被占用 int ct = count.get(); //判断锁是不是自己占用的,做重入 if (ct != 0) { if (Thread.currentThread() == owner.get()) { count.set(ct + 1); return true; } } else { //若count为0 ,表示当前锁未被占用,通过CAS操作 if (count.compareAndSet(ct, ct + 1)) { owner.set(Thread.currentThread()); //如果不是自己,进入队列 return true; } } return false; } @Override public void lock() { if (!tryLock()) { //加入等待队列 waiters.offer(Thread.currentThread()); while (true) { //若线程是队列头部,先判断一次,现在能不能去抢,然后再去加锁 Thread head = waiters.peek(); if (head == Thread.currentThread()) { if (!tryLock()) { LockSupport.park(); } else { waiters.poll(); return; } } else { LockSupport.park(); } } } } public boolean tryUnlock() { if (owner.get() != Thread.currentThread()) { throw new IllegalMonitorStateException(); } else { int ct = count.get(); int nextc = ct - 1; count.set(nextc); if (nextc == 0) { //可重入锁被加锁多次,一旦为0 就释放锁,如果不是0,还得继续释放 owner.compareAndSet(Thread.currentThread(), null); return true; } else { return false; } } } @Override public void unlock() { if (tryUnlock()) { Thread head = waiters.peek(); if (head != null) { LockSupport.unpark(head); } } } /** * 暂时忽略 * * @throws InterruptedException */ @Override public void lockInterruptibly() throws InterruptedException { } /** * 暂时忽略 * * @throws InterruptedException */ @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } /** * 暂时忽略 * * @throws InterruptedException */ @Override public Condition newCondition() { return null; } }
以上就是Java并发Lock接口实现示例详解的详细内容,更多关于Java并发Lock接口的资料请关注脚本之家其它相关文章!