讲解下你自己理解的 CAS 机制 ?
全称 Compare and swap, 即 “比较并交换”. 相当于通过一个原子的操作, 同时完成 “读取内存, 比较是否相等, 修改内存” 这三个步骤. 本质上需要 CPU 指令的支撑.
ABA问题怎么解决?
给要修改的数据引入版本号. 在 CAS 比较数据当前值和旧值的同时, 也要比较版本号是否符合预期.
如果发现当前版本号和之前读到的版本号一致, 就真正执行修改操作, 并让版本号自增; 如果发现当前版本号比之前读到的版本号大, (说明已经修改过了!!) 就认为操作失败.
介绍下 Callable 是什么?
Callable 是一个 interface . 相当于把线程封装了一个 “返回值”. 方便程序猿借助多线程的方式计算结果.
Callable 和 Runnable 相对, 都是描述一个 “任务”. Callable 描述的是带有返回值的任务 , Runnable 描述的是不带返回值的任务.
Callable 通常需要搭配 FutureTask 来使用. FutureTask 用来保存 Callable 的返回结果. 因为Callable 往往是在另一个线程中执行的, 啥时候执行完并不确定 . FutureTask 就可以负责这个等待结果出来的工作.
线程同步的方式有哪些?
synchronized, ReentrantLock, Semaphore 等都可以用于线程同步.
信号量听说过么?之前都用在过哪些场景下?
信号量, 用来表示 “可用资源的个数”. 本质上就是一个计数器.
使用信号量可以实现 “共享锁”, 比如某个资源允许 3 个线程同时使用, 那么就可以使用 P 操作作为加锁, V 操作作为解锁, 前三个线程的 P 操作都能顺利返回, 后续线程再进行 P 操作就会阻塞等待,直到前面的线程执行了 V 操作.
Java中有哪几种方式来创建线程执行任务 ?
1.继承Thread类
这种情况下 , 需要重写里面的run()方法 , 同时占用了继承的名额 , 因为Java中是单继承的 .
package interview;
//创建线程的几种方式
//1.继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("hello thread");
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
2.实现Runnable接口
package interview;
//2.实现Runnable接口
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello thread1");
}
public static void main(String[] args) {
Thread myThread1 = new Thread(new MyRunnable());
myThread1.start();
}
}
3.使用匿名内部类
//3.1 使用匿名内部类
public class MyThread {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("hello thread");
}
};
thread.start();
}
}
public class MyThread {
public static void main(String[] args) {`在这里插入代码片`
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello thread");
}
});
thread.start();
}
}
4.使用Lambda表达式
public class MyThread {
public static void main(String[] args) {
Thread thread = new Thread(() -> System.out.println("hello thread"));
thread.start();
}
}
5.实现Callable接口
package interview;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//4.实现Callable接口
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "override call";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
String result = futureTask.get();
System.out.println(result);
}
}
总结 : 实现Callable接口 , 需要实现call()方法 , 使用Thread + FutureTask结合 .
6.使用线程池
package interview;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//6.使用线程池
public class ThreadPoll implements Runnable{
@Override
public void run() {
System.out.println("thread poll");
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new ThreadPoll());
}
}
为什么不建议使用Executors来创建线程池?
除开有可能造成OOM之外,我们使用Executors来创建线程池也不能自定义线程的名字,不利于排查问题,所以建议直接使用ThreadPoolExecutor来定义线程池,这样可以灵活控制。