- 1.多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
- 2.sleep()和wait()有什么区别?
- 3.讲一下线程的几种实现方式?
- 4.有没有使用过线程并发库?
- 5.线程的状态转换?
- 6.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend((se s ban de))方法为何不推荐使用?
- 7.并发和并行有什么区别?
- 8.线程和进程有什么区别?
- 9.守护线程是什么?
- 10.创建线程有哪几种方式?
- 11.说一下runnable和Callable有什么区别?
- 12.线程有哪些状态?
- 13.sleep(死立P)和wait()有什么区别?
- 14.notify()和notifAll()有什么区别?
- 15.线程的run() 和start(死大t)有什么区别?
- 16.创建线程池有哪几种方式?
- 17.线程池都有哪些状态?
- 18.线程池中submit(死妹t)和execute(a 色q t)方法有什么区别?
- 19.在Java程序中怎么保证多线程的运行安全?
- 20.多线程中synchronized锁升级的原理是什么?
- 21.什么是死锁?
- 22.怎么防止死锁?
- 23.ThreadLocal(乐口)是什么?有哪些使用场景?
- 24.说一下synchronized底层实现原理?
- 25.synchronized和volatile的区别是什么?
- 26.synchronized和lock有什么区别?
- 27.synchronized和ReentrantLock(v n 劝的 辣K)区别是什么?
- 28说一下atomic(er他们K)的原理?
- 29.线程池怎么创建?
- 30.悲观锁和乐观锁的区别有哪些?
1.多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
多线程有两种实现方法:继承Thread(z rue的)类或者实现Runnable(run的bo)接口。
实现同步也有两种方法:一种是同步方法,另一种是同步代码块。
同步方法是在方法返回类型前面加上synchronized(❤亏奈死t)关键字
同步代码块是synchronized (这里写需要同步的对象){…}
2.sleep()和wait()有什么区别?
- Thread类的方法:sleep(),yield(yi 有的)等
Object的方法:wait()和notify()等 - 每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。
sleep方法没有释放锁,
wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。 - wait、notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
- sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
3.讲一下线程的几种实现方式?
.第一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法,然后在run方法里填写相应的逻辑代码。
第二种方法是实现Runnable接口,并编写run方法,相比继承Thread类创建线程的好处是以实现接口的方式创建线程可以对类进行更好的扩展,该类可以继承其他类来扩展自身需求,相比第一种方式更加灵活,扩展性强。
实现Callable接口创建线程与Runnable接口的不同之处在于:如果你想要在线程执行完毕之后得到带有返回值的线程则实现Callable接口4.有没有使用过线程并发库?
在java5之后,就有了线程池的功能了,在介绍线程池之前,先来简单看一下线程池的概念。假设我开了家咨询公司,那么每天会有很多人过来咨询问题,如果我一个个接待的话,必然有很多人要排队,这样效率就很差,我想解决这个问题,现在我雇几个客服,来了一个咨询的,我就分配一个客服去接待他,再来一个,我再分配个客服去接待……如果第一个客服接待完了,我就让她接待下一个咨询者,这样我雇的这些客服可以循环利用。这些客服就好比不同的线程,那么装这些线程的容器就称为线程池。5.线程的状态转换?
新建状态:当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值
就绪状态:当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行
运行状态:如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态
阻塞状态:当处于运行状态的线程失去所占用资源之后,便进入阻塞状态
死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期6.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend((se s ban de))方法为何不推荐使用?
继承Thread类,重写run方法,在调用start方法。
实现Runnable接口,重写run方法。在传给Thread构造器,调用时调用Thread的start方法。
用synchronized关键字修饰同步方法 。
不使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
7.并发和并行有什么区别?
并行:多个处理器或多核处理器同时处理多个任务。
并发:多个任务在同一个CPU核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执行的。
8.线程和进程有什么区别?
一个程序下至少有一个进程,一个进程下至少有一个线程,一个进程下也可以有多个线程来增加程序的执行速度。
9.守护线程是什么?
守护线程是运行在后台的一个钟特殊进程,它独立控制终端并且周期性地执行某种任务或等待处理某些发生的事件,在Java中垃圾回收就是特殊的守护线程。
10.创建线程有哪几种方式?
创建线程有三种方式:
1.继承Thread重写run方法。
2.实现Runnable接口。
3.实现Callable(口嘞bou)接口。
11.说一下runnable和Callable有什么区别?
Runnable没有返回值,Callable可以拿到有返回值,Callable可以看作是Runnable的补充。
12.线程有哪些状态?
线程状态:
NEW尚未启动
RUNNABLE正在执行中
BLOCKED(不洛k t)阻塞的(被同步锁或者IO锁阻塞)
WAITING(为挺)永久等待状态
(太嗯t)TIMED_WAITING等待指定的时间重新被唤醒的状态
TERMINATED(汤姆内te t)执行完成
13.sleep(死立P)和wait()有什么区别?
类的不同,sleep()来自Thread,wait() 来自Object。
释放锁:sleep() 不释放锁,wait()释放锁。
用法不同:sleep()时间到会自动恢复,wait()可以使用notify()/notifyALL() 直接唤醒。
14.notify()和notifAll()有什么区别?
notifyAll() 会唤醒所有的线程,notify()只会唤醒一个线程,notifyAll()调用后,会将全部线程有等待池移动到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待被释放后再次参与竞争,而notify()只会唤醒一个线程,具体唤醒哪一个线程有虚拟机控制。
15.线程的run() 和start(死大t)有什么区别?
start() 方法用于启动线程,run()方法用于执行线程的运行时代码,run()可以重复调用,而start()只能调用一次。
16.创建线程池有哪几种方式?
线程池创建有七种方式,最核心的是最后一种:
• newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无
界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活
动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;
• newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几
个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线
程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线
程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;
• newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后
使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味
着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有
工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;
newSingleThreadScheduledExecutor() : 创 建 单 线 程 池 , 返 回
ScheduledExecutorService,可以进行定时或周期性的工作调度;
• newScheduledThreadPool(int corePoolSize) : 和
newSingleThreadScheduledExecutor() 类 似 , 创 建 的 是 个
ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作
线程还是多个工作线程;
• newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java
8 才加入这个创建方法,其内部会构建 ForkJoinPool,利用 Work-Stealing 算法,并
行地处理任务,不保证处理顺序;
• ThreadPoolExecutor():是最原始的线程池创建,上面 1-3 创建方式都是对
ThreadPoolExecutor 的封装。
17.线程池都有哪些状态?
RUNNING:这是最正常的状态,接收新的任务,处理等待队列中的任务。
SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。
STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。
TIDYING:所有的任务都销毁,workCount为0,线程池的状态在转换为TIDYING状态时,会执行钩子方法terminated()。
TERMINATED:terminated()方法结束后,线程池的状态就会变成这个。
18.线程池中submit(死妹t)和execute(a 色q t)方法有什么区别?
execute():只执行Runnable类型的任务。
submit():可以执行Runnable和Callable类型的任务
Callable类型的任务可以获取执行的返回值,而Runnable执行无返回值。
19.在Java程序中怎么保证多线程的运行安全?
方法一:使用安全类,比如java.util.concurrent(肯卡人t)下的类。
方法二:使用自动锁synchronized。
方法三:使用手动锁lock(辣哥)
手动锁java实例代码:
Lock lock = new ReentrantLock();
lock. lock();
try {
System. out. println(“获得锁”);
} catch (Exception e) {
// TODO: handle exception
} finally {
System. out. println(“释放锁”);
lock. unlock();
}
20.多线程中synchronized锁升级的原理是什么?
synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问
的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进
入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,
如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之
后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程
就构成了 synchronized 锁的升级。
锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化
synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而
减低了锁带来的性能消耗。
21.什么是死锁?
当线程一持有独占锁a,并尝试去获取独占锁b的同时,线程二持有独占锁b,并尝试获取独占锁a的情况下,就会发生一二两个线程由于互相持有对方需要的锁,而发生阻塞现象,这种现象称之为死锁。
22.怎么防止死锁?
尽量使用tryLock(long timeout,TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止锁死。
尽量使用Java.util.concurrent并发类代替自己手写锁。
尽量降低锁的使用力度,尽力不要几个功能用同一把锁。
23.ThreadLocal(乐口)是什么?有哪些使用场景?
ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal的经典使用场景是数据库连接和session管理等。
24.说一下synchronized底层实现原理?
synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的
基本实现单元。在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为
需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。
但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的
monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量
级锁,大大改进了其性能。
25.synchronized和volatile的区别是什么?
volatile是变量修饰符:synchronized是修饰类、方法、代码段。
volatile仅能实现变量的修改可见性,不能保证原子性:而synchronized则可以保证变量的改变可见性和原子性。
volatile(哇了偷)不会造成线程的阻塞,synchronized可能会造成线程的阻塞。
26.synchronized和lock有什么区别?
synchronized可以给类、方法、代码块加锁,而lock只能给代码块加锁。
synchronized不需要手动获取和释放锁,使用简单,发生议程会自动释放锁,不会造成死锁,而lock需要自己加锁和释放锁,如果使用不当没有unLock()去释放锁就会造成死锁。
通过lock可以知道有没有成功获取锁,而synchronized却没办法做到。
27.synchronized和ReentrantLock(v n 劝的 辣K)区别是什么?
synchronized早期的实现比较低效,对比ReentrantLock,大多数场景性能都相差较大。但是java6中对synchronized进行了非常多的改进。
主要区别:
ReentrantLock使用起来比较灵活,但是必须有释放锁的配合动作
ReentrantLock必须手动获取与释放锁,而synchronized不需要手动释放和启动锁
ReentrantLock只适用于代码块锁,而synchronized可用于修饰方法,代码块等。
28说一下atomic(er他们K)的原理?
atomic主要利用CAS(Compare And Wwap)和volatile和naive方法来保证原子操作,从而避免synchronized的高度开销,执行效率大为提升。
29.线程池怎么创建?
- new Single(星狗) Thread Executor(一 se k t)
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 - new Fixed(非死t) Thread Pool(pu o)
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 - new Cached(k 死 t) Thread Pool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。 - new Scheduled(s 给 d o t) Thread Pool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。30.悲观锁和乐观锁的区别有哪些?
悲观锁(Pessimistic Lock),每次去获取数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block(不辣k)直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
(悲观锁就是很悲观的意识,每次获取数据的时候都认为)
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了
路过记得点赞🙃🙃