网站效果代码易优系统助手插件

当前位置: 首页 > news >正文

网站效果代码,易优系统助手插件,深圳市seo网站设计多少钱,三个字的公司名称大气目录 1.乐观锁/悲观锁 2.重量级锁/轻量级锁#xff08;轻量重量是站在加锁开销的角度#xff09; 3.挂起等待锁/自旋锁 4.公平锁/非公平锁 5.可重入锁与不可重入锁 6.读写锁 synchronized 面试题#xff1a;是什么偏向锁#xff1f; 锁的升级#xff1a; 锁消除轻量重量是站在加锁开销的角度 3.挂起等待锁/自旋锁 4.公平锁/非公平锁 5.可重入锁与不可重入锁 6.读写锁 synchronized 面试题是什么偏向锁 锁的升级 锁消除 锁粗化编译器的优化策略 关于锁的一些面试题 CAS 2.CAS实现自旋锁 3.CAS的ABA问题版本号的引入 4.相关面试题CAS 7.Callable接口 8.ReentrantLock 可重入互斥锁 ReentrantLock 和 synchronized 的区别 举例​编辑 如何更好的选择使用synchronized / ReentrantLock锁 面试题 9.信号量Semaphore 10.CountDownLatch 同时等待N个任务结束——锁存器 1.乐观锁/悲观锁 这两个词不是指某个锁而是指某类锁是锁的特点。 乐观锁加锁的时候假设锁的冲突概率不大 —— 接下来围绕锁要做的工作就会更少。一般情况下不会加锁只有在数据提交更新的时候才会对数据进行并发冲突检测如果发现并发冲突了就会返回给用户信息让用户决定如何处置。 悲观锁加锁的时候假设锁冲突的概率很大 —— 接下来围绕锁要做的工作很多。总是假设最坏的情况每次在取数据时都以为数据会修改所以每次访问数据的时候都会加锁。 乐观锁与悲观锁背后要做的事情是不同的。 synchronized 这把锁算是乐观/悲观锁 这是自适应锁初始情况下是乐观锁预估接下来的冲突概率不大当发现锁竞争比较频繁的时候就会自动切换为悲观锁。 在背后会默默统计冲突的次数达到一定次数就会转变为悲观锁。 2.重量级锁/轻量级锁轻量重量是站在加锁开销的角度 重量级锁加锁的开销比较大要做的工作往往更多。 会有大量的用户态内核态的代码切换。很容易引发线程调度。 轻量级锁加锁的开销比较小要做的工作就较少。 尽量在用户态的代码完成实在不行再调用内核态。不太容易引发线程调度。 虽然这两个锁的效果与乐观锁/悲观锁是重叠的站在的角度不一样。 往往悲观锁要做的工作更多乐观锁要做的工作更少。 在重量级锁/轻量级锁之间synchronized也是自适应的。冲突比较严重会变成重量级锁。 不能100%认为 重量级锁与悲观锁等价轻量级锁与乐观锁等价。 3.挂起等待锁/自旋锁 挂起等待锁是悲观锁/重量级锁的一种典型表现。 自旋锁就属于是乐观锁/轻量级锁的一种典型是实现。 按照之前的方式线程在抢锁失败后就会进入阻塞放弃cpu资源等待被再次调用。采用自旋锁就可以在没有抢到锁后一直尝试获取锁直到锁释放后第一时间获取锁。 而挂起等待锁是先让出cpu资源再去做些别的事情等到锁被释放再尝试去加锁。 synchronized是自适应的一开始自旋锁等到一段时间还没有获取到锁就变为挂起等待锁。 轻量级锁是基于 自旋 的方式在jvm内部用户态的代码实现的。 重量级锁是基于 挂起等待 的方式实现的调用操作系统api在内核中实现的。 4.公平锁/非公平锁 假如说女神分手了但是追她的人有一大堆接下来的的人如果是按照先来后到的方式这就是公平锁。如果这些人不按照先来后到按概率均等的方式就是非公平锁。 操作系统内部线程调度是随机的不做任何额外的限制锁就是非公平锁。 如果想要实现公平锁就要依赖额外的数据结构来记录线程的顺序。 锁的公平与非公平没有好坏之分要结合具体的场景。 5.可重入锁与不可重入锁 可重入锁允许一个线程多次获取同一把锁。 不可重入锁只允许加锁一次的线程。 死锁问题 如果一个线程针对同一把锁连续枷锁两次就有可能出现死锁如果是可重入锁就可以避免死锁。 6.读写锁 所谓的读写锁 就是把读写操作分为两个情况。 多线程之间数据的读取不会产生线程安全问题但是数据的写入会有这种问题。 1.两个数据都是读操作直接并发读即可 2.两个数据都要写有线程安全问题 3.一个线程读一个线程写有线程安全问题。 读写锁就是把读操作和写操作区别对待java标准库中提供了ReentrantReadWriteLock.ReadLock 类 表示读锁。这个对象提供了lock/unlock方法 ReentrantReadWriteLock.WriteLock 类表示写锁这个对象也提供了lock/unlock进行加锁解锁 synchronized不是读写锁。 synchronized 1.乐观悲观自适应 2.重量轻量自适应 3.自旋挂起等待自适应 4.非公平锁 5.可重入锁 6.不是读写锁 面试题是什么偏向锁 不是真正的加锁真正的加锁开销比较大偏向锁只是做一个标记标记的过程非常轻量高效。如果出现锁竞争再取消偏向锁的状态进入轻量锁。本质上是推迟了加锁的时机是懒汉思想的体现。 锁的升级 ynchronized的加锁过程刚开始使用时会处于一个“偏向锁的状态”遇到线程之间的锁竞争就会升级到“轻量级锁” 进一步统计竞争出现的频次达到一定层次之后就会升级到“重量级锁”就是为了能够让synchronized很好的使用不同的场景。降低程序员的使用负担。 对于jvm来说锁的升级过程是不可逆的。 锁消除 在编写代码时编译器会对加上的synchronized进行判断合不合适如果没有必要就会把这个所给优化掉。避免了无脑加锁。 锁粗化编译器的优化策略 锁的粒度就是粗和细如果一段逻辑中频发出现加锁解锁编译器jvm会自动进行锁的组化。 开发时使用细粒度的锁就是期望释放锁的时候其他线程能够及时获取锁但实际上如果没有别的线程来抢占这种情况jvm就会自动把锁粗化避免频繁申请释放锁。 关于锁的一些面试题 1.介绍一下读写锁 读写锁就是把读操作 和 写操作 分别进行加锁。 读锁 与 读锁之间不互斥 读锁 与 写锁之间互斥 写锁 与 写锁之间互斥 2.什么是自旋锁为什么要使用自旋锁缺点是什么 如果获取锁失败立即尝试获取锁无限循环知道获取到锁为止第一次获取锁失败紧接着第二次尝试就会到来一旦锁被释放就能第一时间获取到锁。 相比于挂起等待锁优点一旦锁被释放就能第一时间获取到锁更加高效在锁的持有时间较短的场景下非常有用。 缺点如果所得持有时间较长就会非常浪费cpu资源。 3.synchronized是可重入锁吗 是的 可重入锁就是连续加几次锁不会导致死锁 在锁中记录该锁持有的线程身份以及一个计数器记录加锁次数如果发现加锁的线程就是当前锁的持有线程那么就直接计数自增。 CAS 比较内存与寄存器中的内容如果发现相同就将另一个寄存器的值赋值给内存。 说明 一个内存数据 和 寄存器1寄存器2. 比较内存与寄存器1的值如果不相等就不操作 如果相等就将寄存器2的值赋值给内存 CAS的使用场景 1.基于CAS实现”原子类“ 在多线程中int / long 等这些定义的变量的赋值中赋值操作– 都不是原子的 通过对 int long 这些类型的封装从而可以实现原子的操作。 由上图可见在多线程中并没有加锁但是并没有发生线程安全问题因为这个基于CAS的对基本类型进行封装的方法进行运算时可以看作是一个指令·完成的。 这些都是对基本数据类型的封装。 CAS实现原子类的原理 2.CAS实现自旋锁 3.CAS的ABA问题版本号的引入 CAS确实好用但是会有ABA问题。因为CAS在比较的过程中会判断有没有别的线程插入进来执行 而CAS判断是否有这一结果的依据是判断值有没有被修改如果值相同就认为没有别修改如果值不同就认为有线程插入修改了。但是值的相等不相等并不等同于线程有没有插入执行。 通常情况下ABA问题不会带来bug但是有一个极端的例子就是转账 因为余额既能加又能减。所以会发生ABA问题如果只能加不能减或者只能减少就不会发生ABA问题。于是引入一个新的概念“版本号”这就是一个只能增加的整数。 4.相关面试题CAS 1.请你说一下自己理解的CAS 全称Compare and swap 即“比较并交换”相当于一个原子操作同时完成”读取内存比较是否相等修改内存“这三个步骤。也可以是实现模拟自旋锁。 2.ABA问题怎么解决 给要修改的数据引入版本号即比较并交换当前值和就值得同时也要比较版本号是否符合预期如果版本号与预期一致就真正修改并且让版本号自增。如果发现当前版本号比之前的大就修改失败。 7.Callable接口 1.Callable和Runnable 相对都是描述一个任务Callable是描述带有返回值的任务Runnable描述的是不带返回值的任务。 2.Callable通常需要搭配FutureTask使用FutureTask用来保存Callable的返回结果。因为Callbale是在另一个线程里执行的啥时候执行结束不确定TutureTask就负责等待结果出来的工作。 使用Callable计算线程123…….1000 理解Callbale
1.创建一个匿名内部类实现Callble接口Callble带有泛型参数泛型参数表示返回值类型。 2.重写Callable的call方法完成累加的过程直接通过返回值返回计算结果。 3.把Callable实例的call用FtureTask包装一下 4.创建线程把FutureTask的实例传入Thread线程就会执行FutureTask内部的Callable的call方法 5.在主线程中调用futureTask.get()放法获取返回值。futureTask能够阻塞等待新线程执行完毕。 线程的创建方式 1.直接继承Thread 2.使用Runnable 3.使用Callable 4.使用lambda 5.使用线程池 8.ReentrantLock 可重入互斥锁 通过lock/unlock方法来加锁减锁。 trylock超时间加锁如果取不到锁等待一段时间就放弃等待 ReentrantLock lock new ReentrantLock();lock.lock();try{} finally{lock.unlock(); } ReentrantLock 和 synchronized 的区别 1.synchronized是关键字底层是jvm的c代码实现的 ReentrantLock 是标准库提供的类是java代码实现的。 2.synchronized通过控制代码块加锁解锁ReentrantLock通过lock/unlock加锁减锁。 3.ReetrantLock 提供啦tryLock这样的方法在加锁的时候不会阻塞而是等待一段时间后直接返回通过返回值来反馈加锁成功还是失败。 4.synchronized是公平锁。ReentrantLock是默认非公平锁。也可以实现公平锁。 5.ReentrantLock还提供啦“等待通知机制” 基于Condition类能力比wait/notify更强一些。 举例 如何更好的选择使用synchronized / ReentrantLock锁 1.锁竞争不激烈的时候使用synchronized效率更高自动释放。更加方便 2.锁竞争不激烈的时候使用ReentrantLock搭配tryLock 更加灵活的控制锁的行为不是死等。 3.如果需要使用公平锁使用ReentrantLock。 面试题 1.线程同步的方式有哪些 synchronizedReentrantLock,Semaphore等都可以用于线程同步。 2.为什么有了synchronized还需要juc的lock ReentrantLock使用时需要手动释放使用更加灵活。 synchronized申请失败时会死等ReentrantLocck可以通过tryLock的方式等待一段时间后就放弃。 ReentrantLock默认是非公平锁可以通过构造方法传入true开始公平锁 synchronized是通过Object的wait/notify实现等待每次唤醒一个随即等待的线程。ReentrantLock搭配Condition实现等待与唤醒可以更加精确唤醒指定线程。         9.信号量Semaphore 信号量相等于一个计数器通过计数器的衡量可用资源的个数。 申请资源 计数器1释放资源 计数器-1这些操作是原子的可以在多线程环境下使用 值为1的计数器相当于“锁”。 举例 10.CountDownLatch 同时等待N个任务结束——锁存器 好比跑步比赛只有所有选手都过了终点才会公布成绩。 1.构造CountDownLatch实例初话化10表示有10个任务 2.每个任务执行完毕都调用latch.countDown(),在CountDownLatch内部的计数器同时自减 3主线程使用latch.await阻塞等待所有任务都执行完毕相当于计数器为0了 11.多线程环境下使用哈希表 1.Hashtable 只是简单地把关键方法加上关键字 这相当于直接对Hashtable对象本身加锁。 1.如果多个线程访问同一个Hashtable会频繁出发锁冲突。因为每个方法几乎都有synchronized任何一个操作都会触发锁竞争。 2.size属性也是通过synchronized来控制同步的所以更新的也是比较慢的。 3.一旦出发扩容就由该线程完成完成整个扩容创建新的hash表再把所有元素搬进去。这样非常耗时这一系列操作可能是一次put就完成使得这次put的开销非常之大。 2.ConcurrentHashMap 相比于Hashtable做出了一系列的改进与优化。以java1.8为例 1.读操作没有加锁而是使用关键字volatile保证从内存读取结果正确只对写操作进行加锁加锁的方式不是用synchronized也不是整个对象。而是通过“锁桶”以每个链表的头节点作为锁对象大大降低了所冲突的概率。 2.引入CAS原子操作针对修改size这个样的操作借助CAS完成并不会加锁。 3.针对Hash表的扩容进行了特殊的优化ConcurrentHashMap进行“化整为零”不会在一次操作中就把所有的数据搬运而是一次只搬运一部分。此后每次操作都会触发一部分的key搬运最终把key搬运完成。这里的扩容机制需要用到B树查询的开销十分稳定 当新表旧表同时存在时插入操作会插入到新的空间 查询/修改/删除都是需要新表与旧表都要查询的。 网上资料有”分段锁“说法这其实与ConcurrentHashMap早期的思想方式一致只不过是一个锁要管理好几个链表这种实现方式处理冲突的做的还不彻底分段锁的实现方式也更复杂。