AQS
不可重入锁的实现
java
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class MyLock {
AtomicBoolean flag = new AtomicBoolean(false);
Thread owner = null;
AtomicReference<Node> head = new AtomicReference<>(new Node());
AtomicReference<Node> tail = new AtomicReference<>(head.get());
//锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
void lock() {
//下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
if (flag.compareAndSet(false, true)) {
System.out.println(Thread.currentThread().getName()+"直接拿到锁");
owner = Thread.currentThread();
return;
}
Node current = new Node();
current.thread = Thread.currentThread();
while (true) {
//尾插
Node currentTail = tail.get();
if (tail.compareAndSet(currentTail, current)) {
System.out.println(Thread.currentThread().getName()+"加入到了链表");
current.pre = currentTail;
currentTail.next = current;
break;
}
}
while (true) {
//每一次只唤醒head节点的下一个节点
if (current.pre == head.get() && flag.compareAndSet(false, true)) {
owner = Thread.currentThread();
//只有持有锁的线程才能到这步,所以不需要cas
head.set(current);
//断掉这个节点和head结点的关系
current.pre.next = null;
current.pre = null;
System.out.println(Thread.currentThread().getName()+"被唤醒之后拿到锁");
return;
}
LockSupport.park();
}
}
void unlock() {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("当前线程没锁无法释放锁");
}
//只唤醒head节点的下一个节点就可以
Node headNode = head.get();
Node next = headNode.next;
//只有持有锁的线程才能到这步,所以不需要cas
flag.set(false);
if (next != null) {
System.out.println(Thread.currentThread().getName()+"唤醒了"+next.thread.getName());
LockSupport.unpark(next.thread);
}
}
class Node {
Node pre;
Node next;
Thread thread;
Node() {
}
}
}
可重入锁的实现
java
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class MyLock {
AtomicInteger flag = new AtomicInteger(0);
Thread owner = null;
AtomicReference<Node> head = new AtomicReference<>(new Node());
AtomicReference<Node> tail = new AtomicReference<>(head.get());
//锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
void lock() {
if (flag.get() == 0) {
//下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
if (flag.compareAndSet(0, 1)) {
System.out.println(Thread.currentThread().getName() + "直接拿到锁");
owner = Thread.currentThread();
return;
}
} else {
if (owner == Thread.currentThread()) {
//get和increase就像++i 和 i++的关系
System.out.println(Thread.currentThread().getName()+"拿到了重入锁,当前重入次数为"+flag.incrementAndGet());
return;
}
}
Node current = new Node();
current.thread = Thread.currentThread();
while (true) {
//尾插
Node currentTail = tail.get();
if (tail.compareAndSet(currentTail, current)) {
System.out.println(Thread.currentThread().getName() + "加入到了链表");
current.pre = currentTail;
currentTail.next = current;
break;
}
}
while (true) {
//每一次只唤醒head节点的下一个节点
if (current.pre == head.get() && flag.compareAndSet(0, 1)) {
owner = Thread.currentThread();
//只有持有锁的线程才能到这步,所以不需要cas
head.set(current);
//断掉这个节点和head结点的关系
current.pre.next = null;
current.pre = null;
System.out.println(Thread.currentThread().getName() + "被唤醒之后拿到锁");
return;
}
LockSupport.park();
}
}
void unlock() {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("当前线程没锁无法释放锁");
}
int i = flag.get();
if (i > 1) {
flag.set(i - 1);
System.out.println(Thread.currentThread().getName()+"解锁了重入锁,重入次数为:"+(i-1));
return;
}
if(i<=0){
throw new IllegalStateException("重入锁解锁错误");
}
//只唤醒head节点的下一个节点就可以
Node headNode = head.get();
Node next = headNode.next;
//只有持有锁的线程才能到这步,所以不需要cas
flag.set(0);
if (next != null) {
System.out.println(Thread.currentThread().getName() + "唤醒了" + next.thread.getName());
LockSupport.unpark(next.thread);
}
}
class Node {
Node pre;
Node next;
Thread thread;
Node() {
}
}
}
过程:
java
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
public class MyLock {
AtomicBoolean flag = new AtomicBoolean(false);
Thread owner = null;
//锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
void lock() {
while (true) {
if (flag.compareAndSet(false, true)) {
owner = Thread.currentThread();
return;
}
LockSupport.park();
}
}
void unlock() {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("当前线程没锁无法释放锁");
}
while (true) {
if (flag.compareAndSet(true, false)) {
return;
}
}
}
}
现在是一个简单的雏形,但是涉及到休眠和唤醒不知道找到对应线程所以引入链表。
这里还要防止多线程的问题,所以使用了AtomicBoolean针对基本数据类型的原子类和AtomicReference针对引用数据类型的原子类
(多线程涉及的地方很多都要把if改成while来不断尝试)
这里的代码的写法很优雅,我照着视频敲得时候如是说道。
在苏醒的时候要防止虚假苏醒要加上条件才能返回。
再完善下就变成了:
java
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class MyLock {
AtomicBoolean flag = new AtomicBoolean(false);
Thread owner = null;
AtomicReference<Node> head = new AtomicReference<>(new Node());
AtomicReference<Node> tail = new AtomicReference<>(head.get());
//锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
void lock() {
//下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
if (flag.compareAndSet(false, true)) {
System.out.println(Thread.currentThread().getName()+"直接拿到锁");
owner = Thread.currentThread();
return;
}
Node current = new Node();
current.thread = Thread.currentThread();
while (true) {
//尾插
Node currentTail = tail.get();
if (tail.compareAndSet(currentTail, current)) {
System.out.println(Thread.currentThread().getName()+"加入到了链表");
current.pre = currentTail;
currentTail.next = current;
break;
}
}
while (true) {
//每一次只唤醒head节点的下一个节点
if (current.pre == head.get() && flag.compareAndSet(false, true)) {
owner = Thread.currentThread();
//只有持有锁的线程才能到这步,所以不需要cas
head.set(current);
//断掉这个节点和head结点的关系
current.pre.next = null;
current.pre = null;
System.out.println(Thread.currentThread().getName()+"被唤醒之后拿到锁");
return;
}
LockSupport.park();
}
}
void unlock() {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("当前线程没锁无法释放锁");
}
//只唤醒head节点的下一个节点就可以
Node headNode = head.get();
Node next = headNode.next;
//只有持有锁的线程才能到这步,所以不需要cas
flag.set(false);
if (next != null) {
System.out.println(Thread.currentThread().getName()+"唤醒了"+next.thread.getName());
LockSupport.unpark(next.thread);
}
}
class Node {
Node pre;
Node next;
Thread thread;
Node() {
}
}
}
基本的非公平不可重入锁AQS(注释一段代码就变成了公平锁)
java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws InterruptedException {
int count[] = new int[]{1000};
List<Thread> threads = new ArrayList<>();
MyLock myLock = new MyLock();
for (int i = 0; i < 100; i++) {
threads.add(new Thread(() -> {
myLock.lock();
for (int i1 = 0; i1 < 10; i1++) {
count[0]--;
}
myLock.unlock();
}));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println(count[0]);
}
}
对应测试成功变为0
接下来分析考虑可重入:
flag.set(false);
注意这个的位置,这句过后就说明别的线程也能过来竞争了,后面拿到的head就可能不安全
java
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
public class MyLock {
AtomicInteger flag = new AtomicInteger(0);
Thread owner = null;
AtomicReference<Node> head = new AtomicReference<>(new Node());
AtomicReference<Node> tail = new AtomicReference<>(head.get());
//锁的原理是拿不到就在一直阻塞在函数里,拿到了就返回
void lock() {
if (flag.get() == 0) {
//下面这段代码存在会导致成为非公平锁,注释掉就变成了公平锁
if (flag.compareAndSet(0, 1)) {
System.out.println(Thread.currentThread().getName() + "直接拿到锁");
owner = Thread.currentThread();
return;
}
} else {
if (owner == Thread.currentThread()) {
//get和increase就像++i 和 i++的关系
System.out.println(Thread.currentThread().getName()+"拿到了重入锁,当前重入次数为"+flag.incrementAndGet());
return;
}
}
Node current = new Node();
current.thread = Thread.currentThread();
while (true) {
//尾插
Node currentTail = tail.get();
if (tail.compareAndSet(currentTail, current)) {
System.out.println(Thread.currentThread().getName() + "加入到了链表");
current.pre = currentTail;
currentTail.next = current;
break;
}
}
while (true) {
//每一次只唤醒head节点的下一个节点
if (current.pre == head.get() && flag.compareAndSet(0, 1)) {
owner = Thread.currentThread();
//只有持有锁的线程才能到这步,所以不需要cas
head.set(current);
//断掉这个节点和head结点的关系
current.pre.next = null;
current.pre = null;
System.out.println(Thread.currentThread().getName() + "被唤醒之后拿到锁");
return;
}
LockSupport.park();
}
}
void unlock() {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("当前线程没锁无法释放锁");
}
int i = flag.get();
if (i > 1) {
flag.set(i - 1);
System.out.println(Thread.currentThread().getName()+"解锁了重入锁,重入次数为:"+(i-1));
return;
}
if(i<=0){
throw new IllegalStateException("重入锁解锁错误");
}
//只唤醒head节点的下一个节点就可以
Node headNode = head.get();
Node next = headNode.next;
//只有持有锁的线程才能到这步,所以不需要cas
flag.set(0);
if (next != null) {
System.out.println(Thread.currentThread().getName() + "唤醒了" + next.thread.getName());
LockSupport.unpark(next.thread);
}
}
class Node {
Node pre;
Node next;
Thread thread;
Node() {
}
}
}
主要是对加锁和解锁周围的和本来flag在true和false转化变为要对1,0的一些判断
java
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws InterruptedException {
int count[] = new int[]{1000};
List<Thread> threads = new ArrayList<>();
MyLock myLock = new MyLock();
for (int i = 0; i < 5; i++) {
threads.add(new Thread(() -> {
for (int i1 = 0; i1 < 5; i1++) {
myLock.lock();
count[0]--;
}
//可重入锁要保证释放锁的次数和上锁的次数相同
for (int i1 = 0; i1 < 5; i1++) {
myLock.unlock();
}
}));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
System.out.println(count[0]);
}
}
整体来说,实现起来比较烧脑,需要有清晰的逻辑,可以多绕几遍