一.volatile关键字有什么用途,和Synchronize有什么区别
volatile是一个轻量级的Synchronize,保证了共享变量的可见性,能够防止脏读,被volatile关键字修饰的变量,如果值发生了改变,其他线程立刻可见
volatile能保证数据可见性,但是无法保证数据的原子性
Synchronize既能保证数据可见,也能保证数据原子性
场景:
1.volatile关键字不能修饰写入操作依赖于当前的值count++; count+=1; , 不是原子操作,JVM字节码层面不是一部操作
2.volatile可以禁止指令重排,JVM相关的优化没了,效率变低
二.什么是指令重排序
指令重排的分类:编译期重排序和运行时重排序
在JVM编译时期或者CPU执行JVM字节码时期,对现有的指令进行重排序,主要目的为了优化运行速度(在不程序运行结果的前提下)
int a = 3 //1
int b = 5; //2
int c = a + b; //3
虽然说指令重排可以调高程序的执行效率,但是在多线程环境下运行可能会影响到结果
解决办法:内存屏障
内存屏障是一个屏障指令,使CPU对屏障指令之前和之后的内存操作结果都是一样的,相当于一种约束
三.先行发生原则Happens-before
int k = 1; //男人
int j = k; //女人
先行发生原则是Java内存模型中定义的两项操作之间的偏序关系,如果操作A先行发生于操作B,其实就是说在发生操作B之前,操作A所产生的影响能够被B观察到
简单来说:执行顺序的控制,后续代码可以监控到之前代码的所有操作
八大原则:
1.程序次序规则:
在一个线程内,按照代码的顺序,书写在前面的代码优先于发生书写在后面的代码
2.管程锁定原则:
一个unlock操作先行发生于后面对同一个锁的lock操作,注意是同一个锁
3.volatile原则:
对一个volatile变量的写操作先行发生于后面对该变量的读操作
4.线程启动原则:
Thread对象的start()方法优先于此线程的每一个动作
5.线程终止原则:
线程中所有的操作都优先发生于对此线程的终止
6.线程中断原则
对线程的interrupt()方法的调用先行发生于被中断线程的代码检测中断事件的发生 先中断再检测
7.对象终结原则:
一个对象的初始化(构造函数执行完毕)完成优先发生于它的finalize()方法的开始
8.传递性:
int a; //1
int b; //2
int c; //3
如果操作A先行发生于操作B,操作B先行发生于操作C,那么就可以得出来操作A优先发生于操作C
四.线程安全的三要素
1.原子性:原子性说明一个或者多个操作要么都成功,要么都失败,中间不能中断,也不存在上下文切换的问题
原子类可以避免操作数值的原子性问题,在java.util.concurrent.atomic下的原子类
如何保证线程运行的原子性:
解决办法:可以使用sync和Lock将多不操作变为原子操作,但是volatile不能保证原子性
可重入锁:对应前几章讲解案例
2.可见性:
保证多线程下,所有线程对数据操作其他线程是可见的,可以使用volatile和sync
3.有序性:
程序执行得顺序按照代码的先后顺序执行,JVM可能会对指令进行重排,不改变结果的前提下进行重排
并发编程面试题:
1.进程和线程还有协程之间的关系
2.并发和并行之间的区别
并发:指统一时间内,宏观上处理多个任务
并行:指统一时间内,真正上处理多个任务
3.Java中多线程实现的方式
继承Thread类,实现Runable接口,但是除以上两种方法外,还可以通过Callable创建一个带返回值的线程,可以通过线程池进行线程创建
4.Callable和Future模式
5.线程池创建的方式(一般不适用Excecutors.nexxxx创建,一般使用ThreadPoolExecutor)
6.Java当中线程状态有哪些
7.多线程中的常用方法
sleep
yield
join
wait
notify
notifyAll
8.线程状态流程图
9.volatile关键字有什么用途,和Synchronize有什么区别
小点:什么是脏读,为什么发生脏读
10.指令重排和先行发生原则
11.并发编程线程安全三要素
12.进程和线程间调度算法:
先来先服务调度算法
短作业优先调度算法
高响应调度法
时间片轮训算法
优先级调度算法
Java中采用抢占式
13.Java开发中用过哪些锁:
悲观锁
乐观锁
公平锁
非公平锁
可重入锁
不可重入锁
自旋锁
共享锁/s锁/读锁
互斥锁/x锁/排他锁、写锁、独占锁、独享锁
死锁
分段锁
分布式中:
分布式锁
数据库中:
行锁,表锁等