Administrator
发布于 2022-09-21 / 0 阅读
0
0

AQS和JUC-202509120206

AQS和JUC-202509120206

AQS和JUC

ReentrantLock重⼊锁

重⼊锁可以完全替代synchronized关键字。在JDK5.0的早期版本中,重⼊锁的性能远远好于

synchronized,但从JDK6.0开始,JDK在synchronized上做了⼤量的优化,使得两者的性能差距

不⼤。重⼊锁对逻辑控制的灵活性要远远好于synchronized。

重⼊锁常⽤⽅法

void lock():获得锁,如果锁已经被占⽤,则等待。

void lockInterruptibly():获得锁,但优先响应中断。

boolean tryLock()

⾏等待,⽴即返回。尝试获得锁,如果成功,返回true;如果失败则返回false;获得不到锁,则

不进

boolean tryLock(long time, TimeUnit unit):在给定时间内尝试获得锁。

boolean isHeldByCurrentThread():判断担⼼线程是否持有锁。

void unlock():释放锁。

package locktest;

import java.sql.SQLOutput;

import java.util.concurrent.locks.ReentrantLock;

/**

  • 可重入锁测试
  • void lock):获得锁,如果锁已经被占用,则等待。
  • void lockInterruptibly():获得锁,但优先响应中断。
  • boolean tryLock():尝试获得锁,如果成功,返回true;如果失败则返回false;获得不
  • 到锁,则不进

  • 行等待,立即返回。
  • boolean tryLock(long time,TimeUnit unit):在给定时间内尝试获得锁。
  • boolean isHeldByCurrentThread():判断担心线程是否持有锁。
  • void unlock():释放锁。
  • *

  • @author zly
  • @since 2025-04-14
  • */

    public class ReentrantLockTest {

    之所以称之为重⼊锁,就是⼀个线程允许反复进⼊。当然,这⾥的反复仅仅局限于⼀个线程;如

    果同⼀个线程多次获锁,那么在释放锁的时候,也必须释放相同次数。如果释放锁的次数多,那

    会得到⼀个java.lang.IllegalMonitorStateException异常,反之,如果释放锁的次数少,那么相当

    于线程还持有这个锁。如下所示:

    public static int num;

    public static void main(String[] args) throws InterruptedException

    {

    myTest myTest = new myTest();

    Thread t1 = new Thread(myTest);

    Thread t2 = new Thread(myTest);

    t1.start();

    t2.start();

    t1.join();

    t2.join();

    System.out.println("num = "+num);

    }

    static class myTest implements Runnable{

    ReentrantLock reentrantLockTest = new ReentrantLock();

    @Override

    public void run() {

    System.out.println(Thread.currentThread().getName()+"开始执

    行");

    for (int i = 0; i < 10000000 ; i++) {

    reentrantLockTest.lock();

    try {

    num++;

    } finally {

    reentrantLockTest.unlock();

    }

    }

    System.out.println(Thread.currentThread().getName()+"执行完

    毕");

    }

    }

    }

    package locktest;

    中断响应 lockInterruptibly()

    import java.util.concurrent.locks.ReentrantLock;

    /**

  • 可重入锁测试2
  • *

  • @author zly
  • @since 2025-04-14
  • */

    public class ReentrantLockTest1 {

    public static void main(String[] args) {

    new Thread(new task()).start();

    }

    static class task implements Runnable {

    public ReentrantLock reentrantLock = new ReentrantLock();

    @Override

    public void run() {

    reentrantLock.lock();

    System.out.println(Thread.currentThread().getName() + "获得

    锁");

    reentrantLock.lock();

    System.out.println(Thread.currentThread().getName() + "再次

    获得锁");

    try {

    System.out.println(Thread.currentThread().getName() +

    "执行任务");

    } finally {

    reentrantLock.unlock();

    System.out.println(Thread.currentThread().getName() +

    "释放锁");

    reentrantLock.unlock();

    System.out.println(Thread.currentThread().getName() +

    "再次释放锁");

    reentrantLock.unlock();

    System.out.println(Thread.currentThread().getName() +

    "再次释放锁");

    }

    }

    }

    }

    如果使⽤synchronized,要么获得锁,要么保持等待。⽽如果使⽤了重⼊锁,则提供了另⼀种可

    能,那就是线程可以被中断。也就是在等待锁的过程中,程序可以根据需要取消对锁的请求。

    即:

    如果⼀个线程正在等待锁,那么它依然可以收到⼀个通知,被告知⽆须再等待,可以停⽌⼯作

    了。

    可以很好的应对死锁问题。示例如下所示:

    package locktest;

    import java.util.concurrent.TimeUnit;

    import java.util.concurrent.locks.ReentrantLock;

    import static java.lang.Thread.sleep;

    /**

  • 终断响应测试
  • *

  • @author zly
  • @since 2025-04-14
  • */

    public class lockInterruptiblyTest {

    public static ReentrantLock lock1 = new ReentrantLock();

    public static ReentrantLock lock2 = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException

    {

    Thread t1 = new Thread(new ReenteruptiblyTest(lock1, lock2));

    Thread t2 = new Thread(new ReenteruptiblyTest(lock2, lock1));

    t1.start();

    t2.start();

    TimeUnit.MILLISECONDS.sleep(100);

    System.out.println("t1.interrupt()...");

    TimeUnit.MILLISECONDS.sleep(1000);

    System.out.println("t2.interrupt()...");

    t1.interrupt();

    }

    static class ReenteruptiblyTest implements Runnable {

    private ReentrantLock lock1, lock2;

    public ReenteruptiblyTest(ReentrantLock lock1, ReentrantLock

    lock2) {

    this.lock1 = lock1;

    this.lock2 = lock2;

    锁申请等待限时 tryLock(long time, TimeUnit unit)

    除了等待外部通知之外,要避免死锁还有另外⼀种⽅式,就是限时等待。以下⾯为例,线程尝试

    获得锁,如果没有获得锁,则等待5秒钟。如果5秒钟之后依然没有获得锁,则返回false,表示获

    锁失败。

    ryLock()⽅法也可以不带参数直接运⾏。在这种情况下,当前线程会尝试获得锁,如果锁并未被

    他线程占⽤,则申请锁会成功,并⽴即返回true。如果锁被其他线程占⽤,则当前线程不会进

    ⾏等待,⽽是⽴即返回false。这种模式不会引起线程等待,因此也不会产⽣死锁

    }

    @Override

    public void run() {

    try {

    //对lock1加锁

    lock1.lockInterruptibly();

    //获得1ock1的可中断锁

    System.out.println(Thread.currentThread().getName()+",

    加锁成功1-2!");

    //等待1ock1和Lock2分别被两个线程获取。产生死锁现象

    TimeUnit.MILLISECONDS.sleep(100);

    //对lock2加锁

    lock2.lockInterruptibly();

    //获得1ock2的可中断锁

    System.out.println(Thread.currentThread().getName()+",

    加锁成功2-2!");

    } catch (InterruptedException e) {

    System.out.println(Thread.currentThread().getName()+",

    被中断!");

    e.printStackTrace();

    } finally {

    if (lock1.isHeldByCurrentThread()) {

    lock1.unlock();

    }

    if (lock2.isHeldByCurrentThread()) {

    lock2.unlock();

    }

    System.out.println(Thread.currentThread().getName()+",

    释放锁!");

    }

    }

    }

    }

    package locktest;

    import java.sql.Time;

    import java.util.concurrent.TimeUnit;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    /**

  • 锁申请等待限时2
  • *

  • @author zly
  • @since 2025-04-14
  • */

    public class TrtLockTest {

    public static void main(String[] args) {

    Task task = new Task();

    new Thread(task).start();

    new Thread(task).start();

    }

    static class Task implements Runnable {

    public static ReentrantLock lock = new ReentrantLock();

    @Override

    public void run() {

    String name = Thread.currentThread().getName();

    try {

    //if (lock.tryLock(1, TimeUnit.SECONDS)) {

    if (lock.tryLock()) {

    System.out.println(System.currentTimeMillis() + name +

    "获取锁成功");

    Thread.sleep(3000);

    } else {

    System.out.println(System.currentTimeMillis() + name +

    "获取锁失败");

    }

    } catch (InterruptedException e) {

    throw new RuntimeException(e);

    } finally {

    公平锁和⾮公平锁

    在⼤多数情况下,锁的申请都是⾮公平锁。系统只是会从这个锁的等待线程中随机选择⼀个。

    当⼊参为true时,则采⽤公平锁⽅式。要求系统维护⼀个有序队列,因此公平锁的实现成本⽐较

    ⾼,性能相对也⾮常低下。因此,默认情况下,锁是⾮公平的。如果没有特别的需求,也不需要

    使

    ⽤公平锁。

    if (lock.isHeldByCurrentThread()) {

    lock.unlock();

    System.out.println(System.currentTimeMillis() +

    name + "释放锁成功");

    }

    }

    }

    }

    }

    package locktest;

    import java.util.concurrent.locks.ReentrantLock;

    /**

  • 公平锁和非公平锁
  • *

  • @author zly
  • @since 2025-04-15
  • */

    public class FairLockTest {

    public static void main(String[] args) {

    FairLockTask fairLockTask = new FairLockTask();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    new Thread(fairLockTask).start();

    }

    static class FairLockTask implements Runnable {

    //开启公平锁

    ReentrantLock lock = new ReentrantLock(true);

    //开启非公平锁

    Condition重⼊锁的搭配类

    相同点

    Condition和Object的wait()、notify()⽅法的作⽤是⼤致相同的,对应关系如下所示:

    ● Condition.await()--->Object.wait()

    ● Condition.signal()--->Object.notify()

    ● Condition.signalAll()--->Object.notifyAll()

    不同点

    Object.wait()和Object.notify()⽅法是和Synchronized关键字合作使⽤的;⽽Condition是与

    ReentrantLock相关联的。

    Condition常⽤⽅法

    void await():

    会使当前线程等待,同时释放当前锁,当其他线程中使⽤signal()或者signalAll()⽅法时,线程会

    重新获得锁并继续执⾏。或者当线程被中断是,也能跳出等待。这和Object.wait()⽅法很相似。

    void awaitUninterruptibly():

    与await()⽅法基本相同,区别是它不会在等待过程中响应中断。

    long awaitNanos(long nanosTimeout):

    如果nanosTimeout时间内,没有被执⾏signal,则解除等待状态。

    boolean await(long time, TimeUnit unit):

    如果time时间内,没有被执⾏signal,则解除等待状态。

    boolean awaitUntil(Date deadline):

    如果deadline时间内,没有被执⾏signal,则解除等待状态。

    //ReentrantLock lock = new ReentrantLock(false);

    @Override

    public void run() {

    try {

    lock.lock();


    System.out.println(Thread.currentThread().getName()+"获得锁");

    } finally {

    lock.unlock();

    }

    }

    }

    }

    void signal():

    ⽤于唤醒⼀个正在等待中的线程。

    void signalAll():

    ⽤于唤醒所有正在等待中的线程。

    package locktest;

    import java.util.concurrent.locks.Condition;

    import java.util.concurrent.locks.ReentrantLock;

    /**

  • Condition重⼊锁的搭配类
  • *

  • @author zly
  • @since 2025-04-15
  • */

    public class ConditionTest {

    public static ReentrantLock lock = new ReentrantLock();

    public static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException

    {

    new Thread(() -> {

    try {

    lock.lock();

    System.out.println("子线程"+

    Thread.currentThread().getName() + "获得锁");

    condition.await();

    System.out.println( "子线程"+

    Thread.currentThread().getName() + "被唤醒");

    } catch (Exception e) {

    e.printStackTrace();

    } finally {

    lock.unlock();

    System.out.println("子线程"+

    Thread.currentThread().getName() + "释放锁");

    }

    }).start();

    //主线程休眠1秒

    Thread.sleep(1000);

    System.out.println("主线程"+ Thread.currentThread().getName() +

    "休眠");

    lock.lock();

    Semaphore信号量

    ⼴义上说,Semapore信号量是对锁的⼀种扩展;因为⽆论是内部锁synchronized,还是重⼊锁R

    eentrantLock,⼀次都只允许⼀个线程访问某⼀资源,⽽信号量却可以指定多个线程同时访

    问某⼀个资源。

    信息量主要提供了⼀下构造函数,必须要指定信号量的准⼊数,即:同时能申请多少个许可

    public Semaphore(int permits); // permits:准⼊数

    public Semaphore(int permits, boolean fair); // permits:准⼊数,fair:是否公平获得锁

    信息量主要⽅法如下所示

    public void acquire();

    尝试获得⼀个准⼊的许可。若⽆法获得,则线程会等待,直到有线程释放

    ⼀个许可或者当前线程被中断。

    public void acquireUninterruptibly();

    具有acquire⼀样的功能,但是不响应中断。

    public void tryAcquire(); 尝试获得⼀个许可,如果成功就返回true,失败则返回false。

    public void tryAcquire(long timeout, TimeUnit unit); 在指定时间内,尝试获得⼀个许可,如果成

    就返回true,失败则返回false。

    public void release();

    资源访问结束后,释放⼀个许可。

    申请了4个准⼊,循环10个线程,那么将会以4个线程⼀组为单位进⾏执⾏输出

    System.out.println("主线程"+ Thread.currentThread().getName() +

    "获得锁");

    condition.signal();

    System.out.println("主线程"+ Thread.currentThread().getName() +

    "唤醒");

    lock.unlock();

    System.out.println("主线程"+ Thread.currentThread().getName() +

    "释放锁");

    }

    }

    package locktest;

    import java.util.concurrent.Semaphore;

    ReadWriteLock读写锁

    ReadWriteLock是JDK5中提供的读写分离锁。它允许多个线程同时读。但是考虑到数据的完整

    性,

    写写操作和读写操作间依然是需要相互等待和持有锁的。读写锁的访问约束情况如下所示:

    下⾯例⼦中,我们使⽤普通锁,执⾏读写操作:

    (我们使⽤读写锁(只需要将上⾯例⼦中openRWLock=true即可)执⾏读写操作)

    所以,如果在系统中,读操作的次数远远⼤于写操作,那么读写锁就可以发挥最⼤的效果,提升

    统的性能。

    /**

  • 并发编程信号量
  • package locktest;

    import java.util.concurrent.TimeUnit;

    import java.util.concurrent.locks.Lock;

    import java.util.concurrent.locks.ReentrantLock;

    import java.util.concurrent.locks.ReentrantReadWriteLock;

    /**

  • 读写锁
  • *

  • @author zly
  • @since 2025-04-17
  • */

    public class ReadWriteLockTest {

    private static final Lock lock = new ReentrantLock();

    private static final ReentrantReadWriteLock readWriteLock = new

    ReentrantReadWriteLock();

    private static final Lock readLock = readWriteLock.readLock();

    private static final Lock writeLock = readWriteLock.writeLock();

    public static void main(String[] args) {

    boolean flag = true;

    //开启三个线程

    for (int i = 0; i < 3; i++) {

    new Thread(() -> {

    if (flag) {

    excute(readLock, "读取数据");

    } else {

    excute(lock, "读取数据");

    }

    }).start();

    }

    for (int i = 0; i < 3; i++) {

    new Thread(() -> {

    if (flag) {

    excute(writeLock, "写入数据");

    } else {

    excute(lock, "写入数据");

    }

    }).start();

    }

    CountDownLatch倒计时器

    CountDownLatch是⼀个多线程控制⼯具。⽤来控制线程的等待。设置需要countDown的数量

    num,然后每⼀个线程执⾏完毕后,调⽤countDown()⽅法,⽽主线程调⽤await()⽅法执⾏等

    待,

    直到num个⼦线程执⾏了countDown()⽅法 ,则主线程开始继续执⾏。

    }

    private static void excute(Lock lock, String msg) {

    try {

    lock.lock();

    System.out.println(System.currentTimeMillis() / 1000 + " "

    + Thread.currentThread().getName() + msg);

    TimeUnit.MILLISECONDS.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    } finally {

    lock.unlock();

    }

    }

    }

    package locktest;

    import java.util.Random;

    import java.util.concurrent.CountDownLatch;

    import java.util.concurrent.Executor;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    import java.util.concurrent.TimeUnit;

    /**

  • 倒计时器
  • *

  • @author zly
  • @since 2025-04-17
  • */

    public class CountDownLatchTest {

    public static final CountDownLatch countDownLatch = new

    CountDownLatch(3);

    public static void main(String[] args) throws InterruptedException

    {

    ExecutorService executorService =

    Executors.newCachedThreadPool();

    for (int i = 0; i < 3; i++) {

    executorService.submit(new CountDownLatchTask(i,

    countDownLatch));

    }

    countDownLatch.wait();

    System.out.println("所有任务执行完毕");

    executorService.shutdown();

    }

    static class CountDownLatchTask implements Runnable {

    private Integer i;

    private CountDownLatch countDownLatch;

    public CountDownLatchTask(Integer i, CountDownLatch

    countDownLatch) {

    this.i = i;

    this.countDownLatch = countDownLatch;

    }

    @Override

    public void run() {

    try {

    Random random = new Random();

    Integer sleepTime = random.nextInt(1000);

    TimeUnit.MILLISECONDS.sleep(sleepTime);

    if (i == 1) {


    System.out.println(Thread.currentThread().getName() + "子任务发生异常");

    throw new RuntimeException("子任务发生异常");

    }

    System.out.println(Thread.currentThread().getName() +

    "执行完成" + "休眠" + sleepTime);

    } catch (InterruptedException e) {

    throw new RuntimeException(e);

    }

    System.out.println(Thread.currentThread().getName() + "执行

    完成");

    }

    }

    }

    CyclicBarrier循环栅栏

    CyclicBarrier与CountDownLatch⾮常类似,它⽀持计数器的反复使⽤,CyclicBarrier可以

    理解为循环栅栏。CyclicBarrier可以接收⼀个参数作为Runnable barrierAction,每当计数器

    ⼀次计数完成后——CyclicBarrier.await()时,系统会执⾏的动作。

    CyclicBarrier.await()⽅法可能会抛出两种异常:⼀个是InterruptedException,也就是

    在等待过程中,线程被中断,应该说这是⼀个⾮常通⽤的异常,⼤部分迫使线程等待的⽅法都可

    会抛出这个异常,使得线程在等待时依然可以响应外部紧急事件。另外⼀个异常则是CyclicBarrie

    r

    特有的BrokenBarrierException,⼀旦遇到这个异常,则表示当前的CyclicBarrier已经破损

    了,可能系统已经没有办法等待所有线程到⻬了。如果继续等待,可能就是徒劳⽆功的,因此就

    结束吧。

    package locktest;

    import java.util.concurrent.BrokenBarrierException;

    import java.util.concurrent.CyclicBarrier;

    /**

  • 循环栅栏
  • *

  • @author zly
  • @since 2025-04-17
  • */

    public class CyclicBarrierTest {

    public final static Integer NUMS = 3;

    public static CyclicBarrier cyclicBarrier = new

    CyclicBarrier(NUMS, new Teacher());

    public static void main(String[] args) {

    for (int i = 0; i < NUMS; i++) {

    new Thread(new Student(cyclicBarrier, i)).start();

    }

    }

    static class Student implements Runnable {

    private CyclicBarrier cyclicBarrier;

    LockSupport线程阻塞⼯具类

    LockSupport是⼀个⾮常⽅便实⽤的线程阻塞⼯具,它可以在线程内任意位置让线程阻塞。和

    Thread.suspend()相⽐,它弥补了由于resume()在前发⽣,导致线程⽆法继续执⾏的情况。和

    Object.wait()⽅法相⽐,它不需要先获得某个对象的锁,也不会抛出InterruptedException

    private volatile Integer studentNo;

    public Student(CyclicBarrier cyclicBarrier, Integer studentNo)

    {

    this.cyclicBarrier = cyclicBarrier;

    this.studentNo = studentNo;

    }

    @Override

    public void run() {

    try {

    /* if (studentNo ==1) {

    Thread.currentThread().interrupt();

    }*/

    System.out.println("学生" + studentNo + "开始准备考试");

    cyclicBarrier.await();

    System.out.println("学生" + studentNo + "准备交卷");

    cyclicBarrier.await();

    } catch (Exception e) {

    throw new RuntimeException(e);

    }

    }

    }

    static class Teacher implements Runnable {

    private Integer nums = 1;

    @Override

    public void run() {

    if (nums == 1) {

    System.out.println("老师开始发试卷");

    } else {

    System.out.println("老师开始检查试卷");

    }

    nums++;

    }

    }

    }

    异常。

    park()可以阻塞当前线程,其中每⼀个线程都有⼀个许可,该许可默认为[不可⽤]。如果该许可

    是[可⽤]状态,那么park()⽅法会⽴即返回,消费这个许可,将该许可变更为[不可⽤]状态,流

    程代码可以继续执⾏。如果该许可是[不可⽤]状态,那么park()⽅法将会阻塞;

    unpark⽅法,将指定线程的⼀个许可变为[可⽤]状态

    package locktest;

    import java.util.concurrent.TimeUnit;

    import java.util.concurrent.locks.ReentrantLock;

    import static java.lang.Thread.sleep;

    /**

  • 终断响应测试
  • *

  • @author zly
  • @since 2025-04-14
  • */

    public class lockInterruptiblyTest {

    public static ReentrantLock lock1 = new ReentrantLock();

    public static ReentrantLock lock2 = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException

    {

    Thread t1 = new Thread(new ReenteruptiblyTest(lock1, lock2));

    Thread t2 = new Thread(new ReenteruptiblyTest(lock2, lock1));

    t1.start();

    t2.start();

    TimeUnit.MILLISECONDS.sleep(100);

    System.out.println("t1.interrupt()...");

    TimeUnit.MILLISECONDS.sleep(1000);

    System.out.println("t2.interrupt()...");

    t1.interrupt();

    }

    static class ReenteruptiblyTest implements Runnable {

    private ReentrantLock lock1, lock2;

    public ReenteruptiblyTest(ReentrantLock lock1, ReentrantLock

    lock2) {

    this.lock1 = lock1;

    this.lock2 = lock2;

    }


    评论