Lock接口
锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源(但是有些锁可以允许多个线程并发的访问共享资源,比如读写锁)。
在Lock接口出现之前,Java程序是靠synchronized关键字实现锁功能的,而Java 5之后,并发包中新增了Lock接口(以及相关实现类)用来实现锁功能,它提供了与synchronized关键字类似的同步功能,只是在使用时需要显式地获取和释放锁。虽然它缺少了(通过synchronized块或者方法所提供的)隐式获取释放锁的便捷性,但是却拥有了锁获取与释放的可操作性、可中断的获取锁以及超时获取锁等多种synchronized关键字所不具备的同步特性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class X{ private final ReentrantLock lock=new ReentrantLock(); public void m(){ lock.lock(); try{ } finally{ lock.unlock(); } } }
|
使用Reentrantlock
可以进行尝试锁定tryLock()
,这样无法锁定,或者在指定时间内无法锁定,返回false;
使用ReentrantLock
还可以调用lockInterruptibly()
方法,可以对线程interrupt()
方法做出响应,在一个线程等待锁的过程中,可以被打断,打断后会抛异常。
自己实现一个锁
自旋实现锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class SpinLock { AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void mylock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\t come in"); while (!atomicReference.compareAndSet(null, thread)) { } } public void myUnlock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(Thread.currentThread().getName()+"\t invoked myunlock()"); } }
|
缺点:耗费CPU资源,没有竞争到锁的线程会一直占用CPU资源进行CAS操作。
park+自旋实现锁
Java提供了一个较为底层的并发工具类:LockSupport,可以让线程停止下来(阻塞),还可以唤醒线程。
1 2 3 4
| LockSupport.park(Object blocker)
LockSupport.unpark(Thread thread)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class SpinLock { AtomicReference<Thread> atomicReference = new AtomicReference<>(); Queue<Thread> parkQueue = new LinkedBlockingQueue<>();
public void mylock() { System.out.println(Thread.currentThread().getName() + "\t come in"); while (!atomicReference.compareAndSet(null, thread)) { park(); } } public void myUnlock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(Thread.currentThread().getName()+"\t invoked myunlock()"); lock_notify(); } public void park() { parkQueue.add(Thread.currentThread()); LockSupport.park(Thread.currentThread()); } public void unpark() { Thread t = parkQueue.poll(); LockSupport.unpark(t); } }
|
队列同步器AQS
队列同步器AbstractQueuedSynchronizer(AQS)是用来构建锁或者其他同步组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,并发包的作者(Doug Lea)期望它能够成为实现大部分同步需求的基础。
AQS的实现
FIFO队列
同步器依赖内部的同步队列(一个FIFO双向队列)来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成为一个节点(Node)并将其加入同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态。
AQS中的节点Node:
1 2 3 4 5 6 7 8 9 10
| static final class Node { volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; }
|
AQS的属性:
1 2 3 4 5 6 7 8
| public abstract class AbstractQueuedSynchronizer { private transient volatile Node head; private transient volatile Node tail; private volatile int state; }
|
未完待续
本文整理自
Java中的锁及AQS实现原理
仅做个人学习总结所用,遵循CC 4.0 BY-SA版权协议,如有侵权请联系删除!