【并发编程】并发编程

1、在多线程情况下如何保证线程安全
线程安全三要素:
原子性–> 一个操作或者多个操作要么全部执行,要么全部不执行。
有序性–> 代码执行顺序是有序的
可见性–> 当多个线程访问同一变量的时候,其中一个线程修改了这个变量,其他线程必须要立刻知道这个变量被修改的值。[涉及工作内存和主内存]

sychronize保证线程安全;
volitile保证可见性,不保证原子性【如果A去主内存取Num = 0,B同时去取Num=0(有可能也是1)】;

2、synchronized 作用,底层实现
原理:使用操作系统monitorrnter和
讲解:锁升级过程
作用范围:类,方法

3、ReetrantLock 和 synchronized 的区别
jvm内置关键字/基于接口层面实现

4、AQS

5、线程池作用?Java 线程池有哪些参数?阻塞队列有几种?拒绝策略有几种?
查看自己写的文章

6、线程死锁
查看自己写的文章

7、ThreadLocal 是什么,应用场景是什么,原理是怎样的
作用:为每一个线程提供独立的副本变量,解决并发访问的冲突问题。很多情况下,比sychronize解决安全问题更简单
场景:解决数据库连接、Session管理等
内部实现方法

1.public T get() { }
2.public void set(T value) { }
3.public void remove() { }
4.protected T initialValue(){ }

8、介绍一下 Java 有哪些锁(synchronized、juc 提供的锁如 ReentrantLock、CountDownLatch、CyclicBarrier、Semaphore 等)

volitile小专题

1、Java 中能创建 volatile 数组吗?
保护的是数组的引用,但是如果多个线程同时对数据进行修改也会造成线程不安全。
2、volatile 能使得一个非原子操作变成原子操作吗?
不可用保证原子性。但是当修饰long和double,那么其读写都是原子操作;
3、volatile 特性
1)保证可见性,不保证原子性
写一个volitile变量时,JVM会把本地内存的变量强制刷到主内存;
这个写操作导致其他线程中的缓存无效,其他线程读,会从主内存读。volatile的写操作对其它线程实时可见。
【说明:主内存和工作内存】
2)禁止指令重排序
指令重排序是编译器和处理器为了优化程序行程对指令进行排序的一种手段,需要遵守一定规则。

4、使用场景
对于一个变量,只有一个线程执行写操作,其它线程都是读操作,这时候可以用 volatile 修饰这个变量。

5、单例双重锁为什么用到volatile?

public class TestInstance {

private static volatile TestInstance mInstance;

public static TestInstance getInstance(){       //1
    if (mInstance == null){                     //2
        synchronized (TestInstance.class){      //3
            if (mInstance == null){             //4
                mInstance = new TestInstance(); //5
            }
        }
    }
    return mInstance;
}

假如没有用volatile,并发情况下会出现问题,线程A执行到注释5 new TestInstance() 的时候,分为如下几个几步操作:

分配内存
初始化对象
mInstance 指向内存

这时候如果发生指令重排,执行顺序是132,执行到第3的时候,线程B刚好进来了,并且执行到注释2,这时候判断mInstance 不为空,直接使用一个未初始化的对象。所以使用volatile关键字来禁止指令重排序。

6、volatile 原理
JVM底层volatile是采用内存屏障来实现的,内存屏障会提供3个功能:

1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;(符合**happen before** 原则)
2)它会强制将缓存的修改操作立即写到主内存
3)写操作会导致其它CPU中的缓存行失效,写之后,其它线程的读操作会从主内存读。

7、局限性
volatile只能保证可见性,不能保证原子性写操作对其他线程课件,不能解决多个线程同时写问题。

Synchronized小专题
1、Synchronized 使用场景
多个线程同时写一个变量;
前面说到volatile的局限性,多个线程同时写的情况,一般可以使用Synchronized。
2、Synchronized 原理
首先会执行monitorenter指令,然后执行同步代码块中的代码,退出同步代码块的时候会执行monitorexit指令 。
每个对象拥有一个计数器,当线程获取该对象锁后,计数器就会加一,释放锁后就会将计数器减一,所以只要这个锁的计数器大于0,其它线程访问就只能等待。
3、Synchronized锁的升级
偏向锁 -> 锁不存在多线程的竞争,总是由同一线程多次获得,为了让线程获得锁的代价更低而引入了偏向锁。
【当一个线程A访问加了同步锁的代码块时,会在对象头中存 储当前线程的id,后续这个线程进入和退出这段加了同步锁的代码块时,不需要再次加锁和释放锁。】
轻量级锁 -> 在偏向锁情况下,如果线程B也访问了同步代码块,比较对象头的线程id不一样,会升级为轻量级锁,
重量级锁-> 如果线程A和线程B同时访问同步代码块,则轻量级锁会升级为重量级锁,线程A获取到重量级锁的情况下,线程B只能入队等待,进入BLOCK状态。

4、缺点:
不能设置锁超时时间
不能通过代码释放锁
容易造成死锁

ReentrantLock
上面说到 Synchronized的缺点,不能设置锁超时时间和不能通过代码释放锁,ReentranLock就可以解决这个问题。

9、程等待和唤醒的方式
使用Object类中的wait 和 notify方法进行线程的等待和唤醒
使用JUC中的Conditon的await 和singal方法进行线程的等待和唤醒
使用LockSupport的 park和unpark方法阻塞唤醒当前线程
在这里插入图片描述
10、cas
当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为新值,否则就什么都不做。整个比较并替换的操作是一个原子操作

CAS的缺点:
ABA问题是指在CAS操作时,其他线程将变量值A改为了B,但是又被改回了A,等到本线程使用期望值A与当前变量进行比较时,发现变量A没有变,于是CAS就将A值进行了交换操作,但是实际上该值已经被其他线程改变过。
解决办法: 在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A。只要变量被某一线程修改过,变量对应的版本号就会发生递增变化,从而解决了ABA问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值