# 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 是一个特殊的类, 可以把其他变量装进这个类中,  使得 这个类可以在下面两个场景中使用:&#x20;

1. 线程隔离, 给每个线程都分配一个独立的对象, 避免多个线程之间对资源进行抢占。由于每一个对象都被线程独享，也就根本不存在线程安全问题，是一种以空间换取安全性的做法。
2. 在每个线程或者类内都需要保存的 全局变量， 可以用 ThreadLocal, 避免传递参数。

## &#x20;锁相关问题

#### 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 上的指令执行顺序，可能是不同的。某段代码的有序性指的这段代码最终执行的顺序，不会被别的代码插队。
