面试题——JUC多线程

一、创建线程的几种方式?

1.继承Thread类,默认是实现Runnable接口的,重写run方法。
2.实现Runnable接口,重写run方法,无返回值。
3.实现Callable接口,重写call方法,有返回值。
4.通过线程池启动多线程
Runnable 接口 run 方法无返回值;Callable 接口 call 方法有返回值,支持泛型
Runnable 接口 run 方法只能抛出运行时异常,且无法捕获处理;Callable 接口 call 方法允许抛出异常,可以获取异常信息

二、线程池基本原理

线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 new 线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。
在这里插入图片描述

三、线程池有哪些(创建线程池的几种方式)

创建可缓存线程池(newCachedThreadPool)
创建指定线程数量的线程池(newFixedThreadPool)
创建指定长度的线程池(newScheduleThreadPool)
创建单线程化的工作者(newSingleThreadExecutor)

四、线程池参数

核心线程数、最大线程数、线程存活时间、线程存活时间的单位、线程工厂、任务队列、拒绝策略
常见的拒绝策略:
1.直接抛出异常,默认策略
2.直接丢弃任务
3.用调用者所在的线程来执行任务
4.丢弃阻塞队列中最靠前的任务,并执行当前任务
如果提交任务时线程池队列已满会时发会生什么?
线程队列满了,但是最大线程数没有满的话,就会新建一个非核心线程去执行该任务。如果核心线程数、最大线程数、任务队列都满了的话,就会执行线程池的拒绝策略,如果一个任务不能被调度执行那么submit()方法将会抛出一个异常

五、线程安全问题的解决方案

三种方案:
1. 同步代码块
syncnized(锁对象){
可能出现线程安全问题的代码,访问了共享数据的daima
}
注意:a.代码块中的锁对象,可以使用任意对象
b.必须保证多线程使用的锁对象是同一个
c.锁对象将同步代码块锁住,只让一个线程在同步代码块中执行
同步中的线程,没有执行完毕不会释放锁,,同步外的线程没有锁对象,进不去同步
thread.sleep() 进入休眠后会释放CPU的执行权,但不会释放锁对象,只有出了同步代码块才会释放锁对象
2. 同步方法
同步方法也会把方法内部的代码锁住,只让一个线程执行,锁对象就是实现类对象(new Runable(),也就是this)
静态的同步方法,锁对象不能是this,this是创建对象之后产生的,静态优先于对象,静态方法的锁对象是本类的class属性–>class文件对象
3. 加锁
Synchronized和Lock:
在这里插入图片描述

  1. Lock加锁解锁都是由native方法实现的,Synchronized的加锁解锁过程都是由JVM管理的。
  2. 当一个线程使用synchronized获取锁时,而锁被其他线程占用着,那么只能被阻塞,只到成功获取锁。而Lock则提供超时锁和可中断等更加灵活的方式,在未能获取锁的情况下提供一种退出机制。
  3. synchronized不需要手动释放锁,Lock需要在Finally中释放锁,不然容易会造成死锁。
  4. synchronized是非公平锁,ReentrantLock默认是非公平锁,在构造方法传入参数true。
  5. 公平锁:一个线程池里,保证每个线程都能拿到锁,这个锁就是公平锁。

volatile关键字

  1. 保证内存可见性:可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。
  2. 不保证变量的原子性
  3. 禁止指令重排:指令重排序是JVM为了优化指令、提高程序运行效率。
    顺序性:happends-before

synchronized 和 volatile 的区别是什么?

  1. volatile比synchronized更轻量级。
  2. volatile没有synchronized使用的广泛。
  3. volatile不需要加锁,比synchronized更轻量级,不会阻塞线程。
  4. 从内存可见性角度看,volatile读相当于加锁,volatile写相当于解锁。
  5. synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。

sleep和wait的区别

  1. 其他线程调用对象的notify()或者notifyAll()方法式,使用wait()
  2. 超时使用sleep()
  3. sleep()不会释放锁,wait()会释放锁
  4. 有什么办法让wait自己醒过来去抢锁 -----设置时间

悲观锁和乐观锁
悲观锁:悲观的认为总想着有人会修改我的数据,所以为了防止别人修改,每次都需要上锁。导致每次想要拿到数据必须等待拿到锁,所以很浪费时间,如果出现高并发,就会有致命缺陷。
乐观锁:乐观锁总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。
乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

六、GUC提供的多线程工具类有哪些

  1. CountDownLatch 闭锁
  2. CyclicBarrier 循环栅栏
  3. Semaphore 信号量
  4. Exchanger 线程数据交换器

七、多线程情况下,如何保证数据一致性

1.使用synchronized
2.使用Lock锁
3.使用Atomic原子类
4.volatile关键字

八、对线程安全理解(如何保证多线程运行安全)

主要体现在三个方面:原子性、可见性、有序性

  1. 原子性:同一时间只能有一个线程对数据进行操作,其他线程阻塞等待。
  2. 可见性:一个线程修改的状态对另一个线程是可见的。
  3. 有序性:程序执行的顺序按照代码的先后顺序执行 。

导致原因:

  1. 线程切换带来的原子性问题
  2. 缓存导致的可见性问题
  3. 编译优化带来的有序性问题

解决办法:

  1. 解决原子性:JDK Atomic开头的原子类、synchronized、LOCK
  2. 解决可见性:synchronized、volatile、LOCK
  3. 解决有序性:Happens-Before 规则

问题:for循环多次,怎么使用线程保持数据一致

  1. 保证内存可见性:
    可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。
  2. 禁止指令重排:指令重排序是JVM为了优化指令、提高程序运行效率。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值