AbstractQueuedSynchronizer简称AQS,是Java并发容器的一个抽象类,顾名思义抽象同步队列,即队列同步器。
- AQS原理
AQS实现了一个FIFO(FirstIn、FisrtOut先进先出)的队列,底层实现的数据结构是一个双向链表。同步队列,是一个双向链表,包括head节点和tail节点。head节点主要用作后续的调度。 Conditionqueue:非必须,单向链表。当程序中存在cindition的时候才会存在此列表。
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。AQS使用CAS对state同步状态进行原子操作实现对其值的修改。 - ReentrantLock唯一实现了Lock接口
基于AQS有一个同步组件,叫做ReentrantLock。Lock提供了一种方式来实现同步访问,ReentrantLock是唯一实现了Lock接口的类。在这个组件里,stste表示获取锁的线程数,假如state=0,表示还没有线程获取锁,1表示有线程获取了锁。大于1表示重入锁的数量。
Lock相比synchronized主要有如下不同点:
1) synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
2)Lock可以让等待锁的线程响应中断;
3)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到;
4)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。(引用[1])
ReentrantLock锁用法示例如下:
public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock(); //成员变量
public static void main(String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
public void insert(Thread thread) {
lock.lock();
try {
System.out.println(thread.getName()+"得到了锁");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}
}
ReentrantLock获取锁失败示例如下:
public class TestTry {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock(); //成员变量
public static void main(String[] args) {
final TestTry test = new TestTry();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
public void insert(Thread thread) {
if(lock.tryLock()) {
try {
System.out.println(thread.getName()+"得到了锁");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
} else {
System.out.println(thread.getName()+"获取锁失败");
}
}
}
ReentrantLock获取锁中断示例如下:
public class TestInterruptibly {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
TestInterruptibly test = new TestInterruptibly();
MyThread thread1 = new MyThread(test);
MyThread thread2 = new MyThread(test);
thread1.start();
//留时间,让下个Thread得到锁并中断
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.interrupt();
}
public void insert(Thread thread) throws InterruptedException{
System.out.println(thread.getName()+"得到了锁");
lock.lockInterruptibly(); //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
try {
System.out.println(thread.getName()+"lock得到了锁");
long startTime = System.currentTimeMillis();
for( ; ;) {
if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
break;
//插入数据
}
}
finally {
System.out.println(Thread.currentThread().getName()+"执行finally");
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
}
class MyThread extends Thread {
private TestInterruptibly test = null;
public MyThread(TestInterruptibly test) {
this.test = test;
}
@Override
public void run() {
try {
test.insert(Thread.currentThread());
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被中断");
}
}
}
参考:
[1]Java并发编程:Lock
[2]什么是AQS及其原理
[3]Java并发编程原理与实战十七:AQS实现重入锁
网友评论