Java 并发
多线程相关问题
Java实现多线程的方式:
一个类继承 Thread,重写 run 方法,再实例化这个类。
一个类实现Runnable 接口,重写 run 方法,在实例化这个类(更加常用)。
这两个方法有何区别?
Java 不支持多继承,如果继承了 Thread 类,就不可以再去继承别的类。
Thread的构造方法里面,会将 继承Thread 类的子类转换成 Runnable 接口,其内部依然是与 Runnable 一样的形式去实现资源共享。
为什么有了 run 方法还要有 start 方法?
多线程原理:相当于很多小朋友想玩手机,只有一个手机(cpu)。start 方法起到了一个召集大家排队的作用,等真正轮到每一个小朋友时,每一个小朋友内部再用 run方法去玩手机。如果直接用run去调用线程,相当于将线程的调度时机从 操作系统手里面抢回来,相当于由程序员去手动管理多线程。
sleep 与 wait 方法有何区别?
sleep 方法需要指定休眠时间,wait 方法不需要,但是需要被 notify 或者 notifyall 方法唤醒
说一下 Threadlocal
ThreadLocal 是一个特殊的类, 可以把其他变量装进这个类中, 使得 这个类可以在下面两个场景中使用:
线程隔离, 给每个线程都分配一个独立的对象, 避免多个线程之间对资源进行抢占。由于每一个对象都被线程独享,也就根本不存在线程安全问题,是一种以空间换取安全性的做法。
在每个线程或者类内都需要保存的 全局变量, 可以用 ThreadLocal, 避免传递参数。
锁相关问题
synchronized 和 ReentrantLock 有何异同
获得 reentrantlock 后需要手动 上锁和释放锁,而 被synchronized包裹的代码块 则不需要。
两者都是可重入锁
ReentrantLock 在new 的时候可以指定是公平锁还是非公平锁,而synchronized是非公平锁。 公平锁就是等待线程排队,排在前面的先获得锁
ReentrantLock.lockInterruptibly 的方法允许其它线程调用 在等待线程的Thread.interrupt方法来中断在等待线程的等待,并抛出InterruptedException。
说一下 volatile 关键字
被volatile修饰的变量,具有可见性,也就是说被volatile修饰的变量如果被一个线程修改了以后,其他线程立即可以读取到被修改后的变量值,不存在中间有时间间隔导致来不及读取最新的值的情况!
实现可见性的原理如下:在多线程环境下,如果多个线程能够对同一个变量采取读写操作,是这样的:线程将主存中的变量拷贝到自己线程的专属内存里面,然后修改拷贝的变量,修改完以后再把新的变量赋值回主存中。然而在多线程环境中,代码实际的执行顺序是具有不确定性质的,CPU执行的具体指令会被重新排序,可能某一个线程修改完变量以后,还没有来得及放回主存,其他线程就尝试去读。而加了 volatile 关键字以后,则会禁止指令的重排序,确保一旦修改完volatile变量,其他指令不能插队,修改完的变量会被立即放回主存中。
可以将 volatile 看作是一种在指令排序上的一种锁。
并发通用问题
说一说 可见性,原子性,有序性
可见性:一个线程修改了一个公共变量,其他线程立即可见,没有延迟
原子性:操作原子性指 运算操作不能被继续细分。比如 i++ 就不具备原子性,其本质是 i=i+1, 先计算出 i+1的值,再赋值给i, 其实是两步操作,中间可能会被其他线程的操作插队。
有序性:代码的执行顺序和最终 CPU 上的指令执行顺序,可能是不同的。某段代码的有序性指的这段代码最终执行的顺序,不会被别的代码插队。
Last updated