(SUB)LOCK与AQS详解

 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方法返回。

这里写图片描述
 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的核心源码:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值