多线程相关面试题

讲解下你自己理解的 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来定义线程池,这样可以灵活控制。

线程池有哪几种状态 , 每种状态分别表示什么意思 ?

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值