每日一练进击大厂「DAY7」并发编程4

文章目录


一、有三个线程T1,T2,T3如何保证顺序执行

可以使用线程类中的join方法,在一个线程中启动另一个线程,另外一个线程执行完该线程继续执行。T3的run方法中执行t2.join(),T2的run方法中执行t1.join(),这样就会按照T1,T2,T3的顺序执行了。

二、AQS

AQS是java.util.concurrent包下的工具类,全称是AbstractQueuedSynchronizer抽象队列同步器,AQS是多线程同步器,Lock、CountDownLatch、Semaphore都用到了AQS,从本质上来说AQS提供了两种锁机制,分别是排它锁和共享锁。
排它锁:就是存在多线程竞争同一共享资源时,同一时刻只允许一个线程访问该共享资源,也就是多个线程中只能有一个线程获得锁资源,比如Lock中的ReentrantLock重入锁实现就是用到了AQS中的排它锁功能。
共享锁:也称为读锁,就是在同一时刻允许多个线程同时获得锁资源,比如CountDownLatch、Semaphore都是用到了AQS中共享锁功能。

三、CountDownLatch

CountDownLatch是基于执行时间的同步类,允许一个或多个线程等待其他线程完成操作,构造方法接收一个int参数作为计数器,如果要等待n个点就传入n,每次调用countDown方法时计数器减1,await方法阻塞当前线程直到计数器变为0,由于countDown方法可用在任何地方,所以n个点既可以是n个线程,也可以是一个线程里的n个执行步骤。

public class CountDownLatchDemo {
    //主线程等待子线程执行完毕
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    Thread.sleep(5000);
                } catch (Exception e) {

                }

                System.out.println(Thread.currentThread().getName() + "执行了此任务");
                countDownLatch.countDown();

            }).start();
        }
        countDownLatch.await();
        System.out.println("执行主线程代码");
    }

}
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        //子线程等主线程计数器变0一起执行
        CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    countDownLatch.await();
                } catch (Exception e) {

                }
  		 System.out.println(Thread.currentThread().getName() + "执行了此任务");
            }).start();
        }
        Thread.sleep(5000);
        System.out.println("执行主线程代码");
        countDownLatch.countDown();
    }
}

四、CyclicBarrier

循环屏障是基于同步到达某个点的信号量触发机制,作 是让 组线程到达 个屏障时被阻塞,直到最后 个线程到达屏障才会解除。构造 法中的参数表示拦截线程数量,每个线程调 await 法告诉CyclicBarrier 已到达屏障,然后被阻塞。还 持在构造 法中传 个 Runnable 任务,当线程到达屏障时会优先执 该任务。适 于多线程计算数据,最后合并计算结果的应 场景。CountDownLacth 的计数器只能 次, CyclicBarrier 的计数器可使 reset 法重置,所以CyclicBarrier 能处理更为复杂的业务场景,例如计算错误时可 重置计数器重新计算。

五、Semaphore

信号量 来控制同时访问特定资源的线程数量,通过协调各个线程以保证合理使 公共资源。信号量可以 于流量控制,特别是公共资源有限的应 场景, 如数据库连接。Semaphore 的构造 法参数接收 个 int 值,表示可 的许可数量即最 并发数。使 acquire 法获得 个许可证,使 release 法归还许可,还可以 tryAcquire 尝试获得许可。

六、自旋锁

synchronized是重量级锁,拿到锁必须要阻塞,当量大的时候会一直阻塞唤醒,优化一下,不要让其阻塞,只是告诉有这么个标记,在synchronized的边界做循环,这就是自旋,如果做了多次循环发现还没有获得锁,再阻塞。

七、偏向锁

有一个线程来访问代码块,没有锁的竞争,偏向某个线程,把偏向锁的偏向标记存储为线程的线程id(主要是同一个线程反复抢占锁的场景),只有一个线程,如果线程抢占那么就会升级成轻量级锁,偏向锁默认是关闭的。

八、轻量级锁

有线程竞争,自旋去判断这个对象的锁是否释放,自旋次数(默认根据上一次自旋次数和锁的释放时间来决定),会升级重量级锁

九、重量级锁

线程阻塞等待。

十、Synchronized升级流程

首先synchronized会尝试使用偏向锁的方式去竞争锁资源,如果能够竞争到偏向锁,表示加锁成功直接返回。如果竞争锁失败,说明当前锁已经偏向了其他线程,需要将锁升级到轻量级锁,在轻量级锁状态下,竞争锁的线程根据自适应自旋次数去抢占锁资源。如果在轻量级锁状态下还是没有竞争到锁,就只能升级到重量级锁,在重量级锁状态下,没有竞争到锁的线程就会被阻塞,线程的状态就是Blocked。

每日一练进击大厂「DAY7」并发编程4

十一、可重入锁(ReentrantLock)

在运行的某个函数或者代码,因为抢占资源或者中断等原因导致函数或者代码的运行中断,等待中断程序执行结束后,重新进入这个函数或者代码中运行,并且运行结果不会受到影响,那么这个函数或者代码就是可重入的。
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义。线程A在第一次抢占到锁,在还没有释放之前再次得到锁,这个时候就不需要重新抢占锁,而是增加重入次数,然后锁需要被释放两次才能获得真正的释放。
ReentrantLock是一种可重入的排它锁,主要用来解决多线程对共享资源竞争的问题
特性:
1)支持可重入。
2)支持公平和非公平。
3)提供了阻塞竞争锁和非阻塞竞争锁的两种方法,分别是lock()和tryLock()。
几个非常关键的技术:
锁的竞争,ReentrantLock通过互斥变量,使用CAS机制来实现的,没有竞争到锁的线程,使用了AbstractQueuedSynchronized这样一个队列同步器来存储,底层是通过双向链表来实现的,当锁被释放之后,会从AQS队列里的头部唤醒下一个等待锁的线程。
公平和非公平的特性,主要体现在竞争锁的时候,是否需要判断AQS队列存在等待中的线程。等待队列里的锁则是公平锁,如果都可以竞争锁,则是非公平锁。
锁的重入特性,在AQS里面有一个成员变量来保存当前获得锁的线程,当同一个线程下次再来竞争锁的时候,就不会去走锁竞争的逻辑,而是直接增加重入次数。

十二、synchronized和Lock区别

  1. synchronized是关键字,是底层JVM层面实现,Lock是java的juc包下的接口实现。
  2. synchronized异常会释放锁,lock必须手动释放锁。
  3. synchronized不能响应中断,lock可以响应中断。
  4. synchronized可重入,非公平,lock可重入、可公平可非公平。
  5. synchronized通过两种方式来控制锁的粒度一种是修饰在方法层面,另一种是修饰在代码块上。Lock锁的粒度是通过它里面提供的lock()和unlock()方法决定的。
  6. Lock比synchronized灵活性更高,Lock可以自主抉择什么时候加锁,什么时候释放锁,Lock还提供了非阻塞的竞争锁方法tryLock()方法,这个方法通过返回true/fasle来告诉当前线程是否已经有其他线程正在使用锁。
  7. synchronized引入了偏向锁、轻量级锁、重量级锁以及锁升级的方式来优化加锁的性能;Lock则用到了自旋锁的方式来实现性能优化。

十三、乐观锁

对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。

十四、悲观锁

对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,直接上锁操作资源。


总结

择一良人,选一城市,三餐四季,春夏秋冬

展开阅读全文

页面更新:2024-05-15

标签:信号量   队列   屏障   线程   计数器   公平   竞争   代码   方法   资源

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top