AQS
底层实现
JDK
LockSupport
不需要锁对象
通过part和unpart(Thread t)即可暂停和继续,底层调用的是Usafe类的park和unpark方法
AQS使用双向链表,底层是CAS+Volitile用CAS替代了锁整个链表的操作。
cas
addWaiter()
1.给链表加锁,但锁链表效率低,因此采用compareAndSetTail方法,用CAS操作不用给整个链表加锁了
VarHandle:当有了handle后可以用handle操作这个值
意义:
1.普通属性也可以原子操作
2.比反射快,直接操作二进制码
为什么是双向列表:后面的节点需要考虑前面节点的状态
公平锁:上来先排队
非公平锁:上来直接抢锁
state 根据子类不同的实现,取不同的意义。(CoutDownLatch的state表示门闩多少层)
AQS类中有一个内部类Node,里面装的是它的成员变量Thread。很多Node组成一个双向链表,就是一个等待队列。
非公平的时候,如果抢不到锁,就进入队列继续等待。
如果抢到了,就用CAS的方式设定当前线程为独占这把锁的线程。
AQS和Lock接口的关系
Lock接口定义了锁的实现规范,锁类通过聚合AQS来实现锁,然后通过集成Lock接口来实现锁的更多的功能。通过Lock接口和AQS实现了独占锁,共享锁,可重入锁,可重入读写锁,公平锁和非公平锁。LockSupport,和Conditoin实现了一个等待队列,类似于同步锁中的wait和notify方法。这些锁与同步锁不同的是**,首先这些是显示的释放锁和显示的获取锁,不像同步锁是隐式的,这样操作要灵活一点,然后同步锁升级为重量级锁后一旦没抢占到就要进入阻塞状态,而这些锁可以进入阻塞态,也能直接返回,最后,能够超时获取锁,和第一个差不多,如果时间过了则返回。**这里再通过一个独占锁的实现来分析一下这些锁的核心机制,核心机制是一样的,不同的是通过一些方法实现了独占、共享、读写、重入、公平和非公平。首先独占锁的的获取是通过lock方法获取的。
源码解析(https://www.cnblogs.com/waterystone/p/4920797.html)
同步队列
acquire里面首先会通过tryAcquire方法来尝试获取锁,如果获取到就返回,如果没获取到再将当前线程做成一个节点,节点包含了线程的引用,线程的状态,前驱节点,和后继节点,然后将这个节点加入到同步队列中,就像同步锁中的block态一样,这些同步队列里面的线程会通过LockSupport类实现的方法进入到阻塞态。同步队列是由AQS同步器实现的
等待队列
这里核心机制基本完成了一大半,获取锁,释放锁,阻塞状态实现了,还有一小部分就是实现同步锁中的等待和唤醒状态。当使用Condition
的时候,等待队列的概念就出来了。这里是通过LockSupport和Condition实现同步锁中的等待和唤醒,每个Condition对象都包含着一个队列,下面分析condition对象的实现:等待队列、等待和通知。Condition对象是AQS的内部类,每个AQS可以包含多个Condition对象,也就说明可以有多个等待队列,同步锁中只有一个等待队列。等待队列也是一个FIFO的队列,结构如下:
是一个单向链表,等待队列中的节点和同步队列中的节点是共用的,如果线程调用了Conditon.await()方法,那么就会释放锁,进入到等待队列中,成为等待状态。
同步队列与等待队列信息交换
等待
调用condition的await方法,将会使当前线程进入等待队列并释放锁(先加入等待队列再释放锁),同时线程状态转为等待状态。
从同步队列和阻塞队列的角度看,调用await方法时,相当于同步队列的首节点移到condition的等待队列中
通知
调用condition的signal方法时,将会把等待队列的首节点移到同步队列的尾部,然后唤醒该节点。
被唤醒,并不代表就会从await方法返回,也不代表该节点的线程能获取到锁,它一样需要加入到锁的竞争acquireQueued方法中去,只有成功竞争到锁,才能从await方法返回。
![这里写图片描述](https://img-blog.csdn.net/20180423170137626?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2xpdXl1YW5xMTIz/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
Object监视器模型
在Object的监视器模型上,一个对象拥有一个同步队列与一个等待队列,而AQS拥有一个同步队列和多个等待队列。
实现类
Reentrantlock
reentrantlock和sync区别
功能
ReentrantLock的条件锁Condition用法 - 玄冬Wong - ITeye博客
reentrantlock用于替代synchronized,和synchronized监视器锁具有相同的的语义。使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待
使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应,
在一个线程等待锁的过程中,可以被打断
比sync更加灵活、更强大,增加了轮询、超时、中断等高级功能。支持公平锁和非公平锁
1有tryLock方法
获取锁不成功可以继续执行,sync则会wait
try {
locked = lock.tryLock(5, TimeUnit.SECONDS);
System.out.println("m2 ..." + locked);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(locked) lock.unlock();
}
2.有lockInterruptibly方法,
被打断后lockInterruptibly可以获取到锁,再释放后可以用lock方法继续尝试获取锁的
3,可作为公平锁(谁在前面谁执行)
rl:提供condition支持,以及很多函数功能丰富ReentrantLock的条件锁Condition用法 - 玄冬Wong - ITeye博客
释放锁
reentrantlock必须要手动释放锁,经常在finally中进行锁的释放
lock必须手动解锁写在try-finally中
使用syn锁定的话如果遇到异常,jvm会自动释放锁
CountDownLatch
CountDownLatch和Join的对比:
CountDownLatch可以更灵活,因为在一个线程中,CountDownLatch可以根据你的需要countDown很多次。而Join是等待所有join进来的线程结束之后才继续执行被join的线程。
package com.mashibing.juc.c_020;
import java.util.concurrent.CountDownLatch;
public class T06_TestCountDownLatch {
public static void main(String[] args) {
usingJoin();
usingCountDownLatch();
}
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end latch");
}
private static void usingJoin() {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end join");
}
}
CyclicBarrier 循环栅栏
使用场景
比如说一个复杂的操作,需要访问数据库,需要访问网络,需要访问文件,有一种方式是顺序执行,效率非常低。这是一种方式,还有一种就是并发执执行,不同的线程去执行不同的操作,必须是这三个线程全部到位了,才能下一步,这个时候就可以用CyclicBarrier。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class T07_TestCyclicBarrier {
public static void main(String[] args) {
//CyclicBarrier barrier = new CyclicBarrier(20);
CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("满人,发车啦"));
/*CyclicBarrier barrier = new CyclicBarrier(20, new Runnable() {
@Override
public void run() {
System.out.println("满人,发车");
}
});*/
for (int i = 0; i < 100; i++) {
new Thread(() -> {
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
Phaser
Phaser @sinceJDK1.7 CyclicBarrier升级版
应用:遗传算法
很多栅栏,每个栅栏都等齐了再往下走
定义栅栏长度:phaser.bulkRegister(7);
停在栅栏等:phaser.arriveAndAwaitAdvance();
不再参与接下来的栅栏(降低当前栅栏高度):phaser.arriveAndDeregister();
再注册一个(增加当前栅栏高度):phaser.register();
@Override protected boolean onAdvance(int phase, int registeredParties)表示阶段phase注册的栅栏高度为registeredParties
import java.util.Random;
import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
public class T09_TestPhaser2 {
static Random r = new Random();
static MarriagePhaser phaser = new MarriagePhaser();
static void milliSleep(int milli) {
try {
TimeUnit.MILLISECONDS.sleep(milli);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
phaser.bulkRegister(7);//Adds the given number of new unarrived parties to this phaser.
for (int i = 0; i < 5; i++) {
new Thread(new Person("person" + i)).start();
}
new Thread(new Person("新郎")).start();
new Thread(new Person("新娘")).start();
}
static class MarriagePhaser extends Phaser {
@Override
protected boolean onAdvance(int phase, int registeredParties) {
switch (phase) {
case 0:
System.out.println("所有人到齐了!" + registeredParties);
System.out.println();
return false;
case 1:
System.out.println("所有人吃完了!" + registeredParties);
System.out.println();
return false;
case 2:
System.out.println("所有人离开了!" + registeredParties);
System.out.println();
return false;
case 3:
System.out.println("婚礼结束!新郎新娘抱抱!" + registeredParties);
return true;
default:
return true;
}
}
}
static class Person implements Runnable {
String name;
public Person(String name) {
this.name = name;
}
public void arrive() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 到达现场!\n", name);
phaser.arriveAndAwaitAdvance();//Arrives at this phaser and awaits others.
}
public void eat() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 吃完!\n", name);
phaser.arriveAndAwaitAdvance();
}
public void leave() {
milliSleep(r.nextInt(1000));
System.out.printf("%s 离开!\n", name);
phaser.arriveAndAwaitAdvance();
}
private void hug() {
if (name.equals("新郎") || name.equals("新娘")) {
milliSleep(r.nextInt(1000));
System.out.printf("%s 洞房!\n", name);
phaser.arriveAndAwaitAdvance();
} else {
phaser.arriveAndDeregister();//Arrives at this phaser and deregisters from it without waiting for others to arrive.
//phaser.register()
}
}
@Override
public void run() {
arrive();
eat();
leave();
hug();
}
}
}
ReadWriteLock
import java.util.Random;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class T10_TestReadWriteLock {
static Lock lock = new ReentrantLock();
private static int value;
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
static Lock readLock = readWriteLock.readLock();
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read Finish!");
//模拟读取操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write Finish!");
//模拟写操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//Runnable readR = ()-> read(lock);
Runnable readR = () -> read(readLock);
//Runnable writeR = ()->write(lock, new Random().nextInt());
Runnable writeR = () -> write(writeLock, new Random().nextInt());
for (int i = 0; i < 18; i++) new Thread(readR).start();//给读线程上读锁的时候,可以一起读,如果上普通锁的话,不能多个线程一起读
for (int i = 0; i < 2; i++) new Thread(writeR).start();
}
ReadWriteLock:没细看ReentrantReadWriteLock 底层原理_tryreleaseshared 原理_JH灰色的博客-CSDN博客
StampedLock:ReadWriteLock升级版
内部通过AQS实现
读锁时其他线程可以读不可写,也叫共享锁
写锁时其他线程不可以读写,即互斥锁
信号量Semaphore
可以用于限流:最多允许多少个 线程同时在运行,基于许可实现
import java.util.concurrent.Semaphore;
public class T11_TestSemaphore {
public static void main(String[] args) {
//Semaphore semaphore = new Semaphore(2);
Semaphore semaphore = new Semaphore(2, true);//每次允许2个同时执行,true是公平的
//允许一个线程同时执行
//Semaphore semaphore = new Semaphore(1);
new Thread(() -> {
try {
semaphore.acquire();//线程想要继续往下执行,需要获得信号量许可。如果拿不到,会阻塞
System.out.println("T1 running...");
Thread.sleep(200);
System.out.println("T1 running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//释放,其他人可以继续取信号量了
}
}).start();
new Thread(() -> {
try {
semaphore.acquire();
System.out.println("T2 running...");
Thread.sleep(200);
System.out.println("T2 running...");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
Exchanger
可以想象 exchanger
是一个容器,用来在两个线程之间交换变量。
import java.util.concurrent.Exchanger;
public class T12_TestExchanger {
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
new Thread(() -> {
String str = "T1";
try {
str = exchanger.exchange(str);//阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + str);
}, "t1").start();
new Thread(() -> {
String str = "T2";
try {
str = exchanger.exchange(str);//阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + str);
}, "t2").start();
}
}
ReentrantLock,CountDownLatch,CyclicBarrier,Phaser,ReadWriteLock,Semaphore,Exchanger都是AQS(AbstractQueueSynchronizer)实现的
Mutex(互斥锁)
class Mutex implements Lock, java.io.Serializable {
// 自定义同步器
private static class Sync extends AbstractQueuedSynchronizer {
// 判断是否锁定状态
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 尝试获取资源,立即返回。成功则返回true,否则false。
public boolean tryAcquire(int acquires) {
assert acquires == 1; // 这里限定只能为1个量
if (compareAndSetState(0, 1)) {//state为0才设置为1,不可重入!
setExclusiveOwnerThread(Thread.currentThread());//设置为当前线程独占资源
return true;
}
return false;
}
// 尝试释放资源,立即返回。成功则为true,否则false。
protected boolean tryRelease(int releases) {
assert releases == 1; // 限定为1个量
if (getState() == 0)//既然来释放,那肯定就是已占有状态了。只是为了保险,多层判断!
throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);//释放资源,放弃占有状态
return true;
}
}
// 真正同步类的实现都依赖继承于AQS的自定义同步器!
private final Sync sync = new Sync();
//lock<-->acquire。两者语义一样:获取资源,即便等待,直到成功才返回。
public void lock() {
sync.acquire(1);
}
//tryLock<-->tryAcquire。两者语义一样:尝试获取资源,要求立即返回。成功则为true,失败则为false。
public boolean tryLock() {
return sync.tryAcquire(1);
}
//unlock<-->release。两者语文一样:释放资源。
public void unlock() {
sync.release(1);
}
//锁是否占有状态
public boolean isLocked() {
return sync.isHeldExclusively();
}
}
Mutex是一个不可重入的互斥锁实现。锁资源(AQS里的state)只有两种状态:0表示未锁定,1表示锁定。下边是Mutex的核心源码: