目录
8、锁Synchronized和reentranLock的对比
13、LinkedBlockingQueue和ArrayBlockingQueue的区别
17、volatile 变量是什么?volatile 变量和 atomic 变量有什么不同
18、volatile 类型变量提供什么保证?能使得一个非原子操作变成原子操作吗?
问题整理
1、什么是线程和进程,他们的区别和联系?
进程是指运行的程序,比如计算机上运行起来的一个应用就是一个进程。进程是由线程组成的,一个进程可以包含多个线程,比如QQ聊天进程由一个发送线程+一个接收线程组成。而线程只是CPU的运行单位。
区别:
- 进程是资源分配的最小单位,而线程是程序执行的最小单位;
- 资源:进程是资源分配的单位,系统分配资源的时候给每个进程单独资源,但线程是资源共享的,一个进程下的线程共享本进程的资源;
- 地址空间:每个进程都有一块自己的独立地址空间,而线程只是最为一个入口,地址空间是共享的;
- 进程之间相互独立,一个进程崩溃不会影响其他进程;但同一进程下的线程崩溃会影响本进程下的其他线程;
联系:
- 一个进程可以包含多个线程;
- 在没有实现线程的操作系统中,进程是资源分配的基本单位,又是调度的基本单位,它是系统中并发执行的单元。在实现线程的操作系统中,进程是资源分配的基本单位,但线程是基本调度单元,是系统并发执行的单元。
2、线程创建的方式有几种?分别是什么?如何启动一个线程?
答案参考上篇博客:https://blog.csdn.net/weixin_44187963/article/details/103459524
3、线程的有几种状态?分别是什么?
线程有6种状态,分别是:
new状态:新建了一个线程对象,但还没有启动
runnable(就绪状态):线程对象创建后调用start方法启动线程,此时线程就处于runnable状态
blocked(阻塞状态):1)等待一个监视器锁来进入同步代码块或同步方法
2)调用Object.wait()方法,此时需要notify/notifyAll去唤醒它
waiting(等待状态):Object.wait()/notify/notifyAll,Thread.join(), LockSupport.park()
timed_waiting:Object.wait()/notify/notifyAll,Thread.join(), LockSupport.park()
terminated(终止状态):线程执行完了之后就会进入到终止状态
4、线程间几种状态的转换?
5、线程的优先级
1)线程优先级的取值范围:1到10
默认优先级=5 优先级由高到低10 9...2 1 优先级高的率先完成任务
2)设定线程的优先级:thread.setPriority(1)
3)优先级特性:
规则性:优先级高的率先完成任务的纪律更大;
随机性:CPU会尽量将资源分配给优先级高的,所以我们看到的结果并不是一定优先级高的先执行完;
继承性:在A线程下创建一个B线程,那么B线程的优先级==A线程的优先级
6、用户线程和守护线程的区别
结束时机:用户线程结束时run方法执行完;而守护线程是等待所有用户线程执行完毕之后;
守护线程使用场景:---GC:Java对象(垃圾)回收机制
7、ReentrantLock可中断锁
ReentrantLock实现的前提就是AbstractQueuedSynchronizer,简称AQS,是java.util.concurrent的核心。AQS是基于FIFO队列的实现AQS中的队列是由 Node节点组成的双向链表实现的。所有的操作都是在这个AQS队列当中。如果一个线程获取锁成功那就成功了,如果失败了就将其放入等待队列当中。
8、锁Synchronized和reentranLock的对比
1)本质:synchronized是一个关键字,reentrantLock是一个类;
2)加锁方式:synchronize只提供阻塞的加锁方式,加锁解锁过程是由jvm控制;reentrantLock提供更加丰富的加锁方式,加锁解锁需要手动调用相应的方法;
3)公平性:synchronized:非公平的锁;reentrantLock:既可以实现公平的锁、也可以实现非公平的锁:实现方法为改变改变构造函数中的参数,传true实现为公平,不传或者传false就实现为非公平的;
4)synchronized:是非重入锁; reentrantLock:是重入锁;
5)实现原理(都是用了CAS操作):synchronized是mark word 和 monitor,reentrantLock借助state 和AQS队列;
6)和读写锁的对比:
synchronized和读写锁对比:读读不互斥,读写互斥,写写互斥;
reentrantLock和读写锁对比:读读不互斥,读写互斥,写写互斥;
9、怎么让线程按照顺序执行
优先级不可以,所以用join()插队的方式实现线程的执行顺序
10、设计线程的标记位可以在任何时间break掉
线程正常结束是:执行完run
设置断点结束一个线程的时候,设置一个flag变量,一定得加volatile--强制进行同步,监听工具 private volatile boolean flag = false;
11、死锁产生条件
1)互斥:A线程和B线程,它们同时请求一部分资源,只有其中一个能拿到
2)请求保持:A线程和B线程,A和B都需要拿到1和2资源才能执行,所以AB分别拿了12那么AB都处于请求保持
3)循环等待:假如AB分别拿了12,那么它们每人占用一个资源同时等待另一个资源释放,所以都处于循环等待
4)不可分割:A在拿到资源之后如果没有完成任务之前资源不能被别的线程抢占
12、怎么设计一个死锁
syncharnized实现,需要有两个锁 lock1和lock2
1.首先需要有两个或两个以上的线程和资源
2.A和B都要保证把lock1和lock2都拿到才能执行后续代码
3.A先拿到lock1且B先拿到lock2
13、LinkedBlockingQueue和ArrayBlockingQueue的区别
(1)方法:实例化对象时,ArrayBlockingQueue必须传参数,LinkedBlockingQueue可不传参数。
(2)底层:ArrayBlockingQueue底层是数组(适合随机访问,有容量),LinkedBlockingQueue底层是链表(适合插入删除,可制定上限);
(3)锁:ArrayBlockingQueue用的是单锁,LinkedBlockingQueue用的是双锁;
(4)效率:ArrayBlockingQueue用的是单锁效率较低,LinkedBlockingQueue用的是双锁效率较高;
14、sleep() 和 wait() 有什么区别
sleep 就是正在执行的线程主动让出 cpu,cpu 去执行其他线程,在 sleep 指定的时间过后,cpu 才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep 方法并不会释放锁,即使当前线程使用 sleep 方法让出了 cpu,但其他被同步锁挡住了的线程也无法得到执行。wait 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了 notify 方法(notify 并不释放锁,只是告诉调用过 wait 方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。如果 notify 方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在 notfiy 方法后增加一个等待和一些代码,看看效果),调用 wait 方法的线程就会解除 wait 状态和程序可以再次得到锁后继续向下运行。
15、请说出你所知道的线程同步的方法。
wait():使一个线程处于等待状态,并且释放所持有的对象的 lock。 sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉 InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且不是按优先级。
notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
16、什么是线程饿死,什么是活锁?
当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI 中线程活锁可能发生在以下情形:
• 当所有线程在序中执行 Object.wait(0),参数为 0 的 wait 方法。程序将发生
活锁直到在相应的对象上有线程调用 Object.notify() 或者 Object.notifyAll()。
• 当所有线程卡在无限循环中。
17、volatile 变量是什么?volatile 变量和 atomic 变量有什么不同
volatile 则是保证了所修饰的变量的可见。因为 volatile 只是在保证了同一个变量在多线程中的可见性,所以它更多是用于修饰作为开关状态的变量,即 Boolean 类型的变量。
volatile 多用于修饰类似开关类型的变量、Atomic 多用于类似计数器相关的变量、其它多线程并发操作用 synchronized 关键字修饰。
volatile 有两个功用:
• 这个变量不会在多个线程中存在复本,直接从内存读取。
• 这个关键字会禁止指令重排序优化。也就是说,在 volatile 变量的赋值操作后面会有一个内存屏障(生成的汇编代码上),读操作不会被重排序到内存屏障之前。
18、volatile 类型变量提供什么保证?能使得一个非原子操作变成原子操作吗?
volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。 在 Java 中除了 long 和 double 之外的所有基本类型的读和赋值,都是原子性操作。而 64 位的 long 和 double 变量由于会被 JVM 当作两个分离的 32 位来进行操作,所以不具有原子性,会产生字撕裂问题。但是当你定义 long 或 double 变量时,如果使用 volatile 关键字,就会获到(简单的赋值与返回操作的)原子性。
19、什么是线程池
线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理。如果每个请求都创建一个线程去处理,那么服务器的资源很快就会被耗尽,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
20、为什么使用线程池?线程池的作用
创建线程和销毁线程的花销是比较大的,这些时间有可能比处理业务的时间还要长。这样频繁的创建线程和销毁线程,再加上业务工作线程,消耗系统资源的时间,可能导致系统资源不足。
线程池作用就是限制系统中执行线程的数量,1、提高效率 创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。2、方便管理 可以编写线程池管理代码对池中的线程同一进行管理,比如说启动时有该程序创建100个线程,每当有请求的时候,就分配一个线程去工作,如果刚好并发有101个请求,那多出的这一个请求可以排队等候,避免因无休止的创建线程导致系统崩溃。
21、几种常见的线程池及使用场景
1、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2、newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3、newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
4、newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
22、线程池中的几种重要的参数
corePoolSize:就是线程池中的核心线程数量,这几个核心线程,只是在没有用的时候,也不会被回收
maximumPoolSize:就是线程池中可以容纳的最大线程的数量
keepAliveTime:就是线程池中除了核心线程之外的其他的最长可以保留的时间,因为在线程池中,除了核心线程即使在无任务的情况下也不能被清除,其余的都是有存活时间的,意思就是非核心线程可以保留的最长的空闲时间,
util:就是计算这个时间的一个单位。
workQueu:就是等待队列,任务可以储存在任务队列中等待被执行,执行的是FIFIO原则(先进先出)。
threadFactory:就是创建线程的线程工厂。
handle:是一种拒绝策略,我们可以在任务满了之后,拒绝执行某些任务。
23、线程池的工作流程