您的位置:首页 > 财经 > 金融 > 怎么用dw制作网站_济南建站都选企汇优先做后付_本周的新闻大事10条_某产品网络营销推广方案

怎么用dw制作网站_济南建站都选企汇优先做后付_本周的新闻大事10条_某产品网络营销推广方案

2025/3/10 6:46:38 来源:https://blog.csdn.net/2301_79083481/article/details/146105595  浏览:    关键词:怎么用dw制作网站_济南建站都选企汇优先做后付_本周的新闻大事10条_某产品网络营销推广方案
怎么用dw制作网站_济南建站都选企汇优先做后付_本周的新闻大事10条_某产品网络营销推广方案

今天我们开始学习多线程的进阶部分啦,这部分很像八股文,我们其实可能应用的很少,但是可能面试我们会被问到的,来学习吧;

1,常见锁策略

锁策略呢,并不是java独有的,如果需要我们自行去实现一个锁,我们就要遵循锁策略,但是synchronized和ReentrantLock实际上已经很够用了,技多不压身,下面正式开始介绍;

1)乐观锁和悲观锁

乐观锁和悲观锁呢,指的是当前锁具有乐观和悲观的特性,而不是说某一个特定的锁,他们都是加锁时候的场景,会判断锁的处境是乐观还是悲观的;

乐观:就是在加锁的时候,预测接下来的锁竞争并不激烈,不需要做额外的工作,不真正加锁,直接访问;

悲观:加锁的时候,预测接下来的锁竞争很激烈,需要做一些工作来解决,访问共享数据之前先加锁;

乐观锁的实现可以引入一个版本号,借助版本号来识别当前数据的版本号是否存在访问冲突;

悲观锁的实现就是先加锁(操作系统的mutex),获取到锁在真正的操作数据;

2)重量级锁和轻量级锁

轻量级锁和重量级锁就是遇到乐观场景和悲观场景的解决方案;

轻量级锁:在乐观的情况下,付出更高的代价,低效,大量内核态用户切换,很容易引发线程调度;

重量级锁:在悲观的情况下,付出更小的代价,高效,少量内核态用户切换,不容易引发线程调度;

大家还记得内核态和用户态我们在哪里讲过吗,没错(我才你答对了),就是线程池,我们之前说为啥要引出线程池呢,有一个原因就是,自己创建线程是需要到内核态去的,是不可控的,而使用线程池是在用户态,是我们可控的;

3)挂起等待锁和自旋锁

挂起等待锁呢就是重量级锁的典型实现,自旋锁就是轻量级锁的典型实现

挂起等待锁:操作系统内核级别的,加锁的时候发现竞争,就会让线程进入阻塞状态,这个阻塞是不占用cpu的,后续的唤醒也需要内核来唤醒,即使当前的锁没有竞争了,我们也不会直接拿到它,会一直阻塞;

自旋锁:应用程序级别的,加锁的时候发现竞争,不会让线程进入阻塞状态,会一直占用cpu,会一直忙等;

4)普通互斥锁和读写锁

普通互斥锁就是锁的特性,一个线程占有了当前的锁,其他的锁就要阻塞等待并且不可剥夺

读写锁呢就是读加锁和读加锁直接是不互斥的读加锁和写加锁之间是互斥的写加锁和写加锁之间也是互斥的,有同学可能会有疑问,加锁不是为了保证线程安全吗,读加锁和读加锁之间不互斥难道没有线程安全吗,读操作是原子的,不涉及到修改,所以是没有问题的;

读写锁是适用于特定场景下的,适用于读多写少的情况下,比如写小说,写操作只有作者一人,而读就有很多很多了,读加锁和写加锁操作互斥为了保证不读一个不完整的数据;

5)可重入锁和不可重入锁

我们之前讨论死锁的时候说过,一个线程一把锁怎么造成死锁,要获取锁的时候再次获取锁,让两个锁达成“2:我要锁,1:不行,你先上一边去,我要过去就能释放锁了,2:你先把锁给我我才能走开啊,1:我得先走开才能释放啊‘’,他俩就这样一直僵持着;

造成这种情况就因为这个锁具有不可重入的特性,而可重入锁就不会有这样的情况;

一个线程一把锁,可以连续多次加锁;

6)公平锁和非公平锁

公平锁就是先来后到,谁先来的,谁就获得当前的锁;

非公平锁就是概率均等,什么时候来,你获得锁的概率就是一样的;

可能感觉概率均等才是公平的,但是你把锁想像成打饭阿姨你就觉得公平锁真不错;

_____________________________________________________________________________

2,CAS

1,什么是CAS

CAS 即 compare and swap 比较和交换,CAS是CPU上的一条指令,它有三个参数,一个是内存地址,另两个都是寄存器的值,我们要表内存地址和寄存器1的值是否相等,如果相等就把寄存器2的值赋值给内存地址;

2,CAS有哪些应用
1)实现原子类

我们之前讲过,我们要保证原子性要去对某些操作加锁,但是我们刚才谈到CAS是原子的,我们就用它构造出了很多可以具有原子性的类,来代替基本的类型;

public class Demo1 {public static void main(String[] args) throws InterruptedException {AtomicInteger a = new AtomicInteger();Thread thread = new Thread(()->{for (int i = 0; i < 10000; i++) {a.incrementAndGet();}});Thread thread1 = new Thread(()->{for (int i = 0; i < 10000; i++) {a.incrementAndGet();}});thread.start();thread1.start();thread.join();thread1.join();System.out.println(a.get());}
}

方便吧;

2)实现自旋锁

CAS还能实现自旋锁的伪代码,为啥是伪代码,CAS毕竟CPU上的指令,java代码中没有具体的代码让我们看到,我们用伪代码的方式来呈现,

public class Demo1 {private Thread t1 = null;public void lock(){while(!CAS(this.t1,null,Thread.currentThread())){};}public void unlock(){this.t1 = null;}
}

还记得什么是自旋锁吗,我们如果没有拿到当前线程会一直处于忙等的状态,直到拿到线程为止,我们这个代码就是,我们创建了一个线程,当这个线程要加锁的时候,我们看看当线程是不是为null,看看这个锁是不是被其他线程占有,如果t1不为null,那就继续循环,继续等待,直到,没人占用这个锁,就对当前线程加锁; 

3,CAS的ABA问题
1)什么是ABA问题

我们学过,CAS是CPU的一段指令,它的工作原理是比较和交换,既然是原子的那就完全没有线程安全问题了吗,我们来举一个极端的例子,我们把A改成B,之后另一个线程过来吧B改成A,我们第三个线程过来的时候看此时数据还是A,那不就是没改吗,可实际代码已经改过一次了;

2)ABA问题引来的BUG

我们在通常情况下ABA问题带来的影响是不大的,但是存在一种极端情况,我们现在有1000块钱,我们去ATM取钱,我们通常会狂按几下那个取款键,

3)解决方案

我们可以引入版本号,版本号只能加不能减,这样就能有效避免ABA问题了,类似一种标记吧;        

_____________________________________________________________________________

3,synchronized原理

1,基本特点

synchronized是自适应锁,JVM会统计每个锁的竞争激烈程度,如果竞争激烈,就会变成重量级的挂起等待锁(悲观),反之就是轻量级的自旋锁(乐观),

synchronized还是互斥锁,非公平锁,可重入锁;

2,加锁工作过程

锁升级,synchronized为啥优秀呢,它是能一步步升级为了应对不同的情况;

0)无锁

没加锁,还没进入synchronized的代码块;

1)偏向锁

进入synchronized之后并不真正的加锁,而是做一个标记,标记非常轻量,效率很高;

如果运行过程中,没有任何线程来抢竞争这个锁,一直运行到当前代码,最终解释代码时也只是清除标记,并没有涉及到真正的加锁和解锁;

2)轻量级锁

如果当前synchronized是偏向锁的时候,有锁想要竞争了,此时由只是做一个标记改为轻量级锁,赶在其他锁之前抢占它,其他锁去阻塞等待;

3)重量级锁

JVM发现锁的竞争十分激烈的时候就会把轻量级锁转换为重量级锁;

synchronized是不能降级的,只能升级;

我们来举一个好玩的例子,是我的老师给我们举的例子,比如我们是一个女生,我们和一堆男生搞暧昧,但是不确定关系,我们搞暧昧这个行为就相当于偏向锁,没有真正的加锁,但是当我们感觉到我们搞暧昧的男生要被人追走了的时候,我们就立刻和他确定关系,来完成加锁;

3,其他的优化操作
锁消除

锁消除也是编译器优化的一种体现,比如我们此时有一个线程,我们对代码去加锁,显然是没有必要的,虽然我们的代码上是这么写的,但是我们JVM在代码运行时就把他优化掉了;

锁粗化

锁的粒度,加锁和解锁中间包含的代码越多(这里只运行时间,执行的指令),锁的粒度就越粗,

什么意思呢,来看代码;

       synchronized (object){count++;}synchronized (object){count++;}synchronized (object){count++;}return count;}

这就属于代码细粒度,JVM就会把让他粗度化,

    synchronized (object){count++;count++;count++;}

这样的,这下大家理解了吧,编译器会帮助我们优化无意义的锁竞争, 给大家讲一个例子,我们给领导打电话的时候,我们要汇报工作情况,我们打电话,跟领导说,我第一个任务做的怎么怎么样,领导说好,我们挂断电话,之后再打电话,跟领导说我第二个任务做的怎么怎么样,领导说嗯,我们挂掉电话,继续打,跟领导说我们第三个任务........,领导..............;哈哈哈哈哈,是不是感觉到不合理了,我们应该一次电话把三次工作全部汇报完,这就是锁粗化存在的意义;

_____________________________________________________________________________

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com