- 1、进程和线程的区别?
2、什么是原子性、可见性、有序性?
3、为什么要使用多线程?
4、创建线程有哪几种方式?
5、什么是守护线程?
6、线程的状态有哪几种?怎么流转的?
7、线程的优先级有什么用?
8、我们常说的 JUC 是指什么?
9、i++ 是线程安全的吗?
10、join 方法有什么用?什么原理?
11、如何让一个线程休眠?
12、启动一个线程是用 start 还是 run 方法?
13、start 和 run 方法有什么区别?
14、sleep 和 wait 方法有什么区别?
15、Thread.yield 方法有什么用?
16、yield 和 sleep 有什么区别?
17、怎么理解 Java 中的线程中断?
18、你怎么理解多线程分组?
19、你怎么理解 wait、notify、notifyAll?
20、同步和异步的区别?
21、什么是死锁?
22、怎么避免死锁?
23、什么是活锁?
24、什么是无锁?
25、什么是线程饥饿?
1、进程和线程的区别?
进程是对运行时程序的封装,是系统进行资源调度和分配的的基本单位,实现了操作系统的并发; 线程是进程的子任务,是CPU调度和分派的基本单位,用于保证程序的实时性,实现进程内部的并发;线程是操作系统可识别的最小执行和调度单位。 每个线程都独自占用一个虚拟处理器:独自的寄存器组,指令计数器和处理器状态。
2、什么是原子性、可见性、有序性?
原子性:一个操作或多个操作要么全部执行完成且执行过程不被中断,要么就不执行。
可见性:当多个线程同时访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
有序性:程序执行的顺序按照代码的先后顺序执行。
1、原子性
在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断,要么执行,要么不执行。
X=10; //原子性(简单的读取、将数字赋值给变量)
Y = x; //变量之间的相互赋值,不是原子操作
X++; //对变量进行计算操作
X = x+1;
语句2实际包括两个操作,它先要去读取x的值,再将y值写入,两个操作分开是原子性的。合在一起就不是原子性的。
语句3、4:x++ x=x+1包括3个操作:读取x的值,x+1,将x写入
注:可以通过 synchronized和Lock实现原子性。因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。
2、可见性
Java提供了volatile关键字保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值立即被其他的线程看到,即修改的值立即更新到主存中,当其他线程需要读取时,它会去内存中读取新值。
Synchronized和Lock也可以保证可见性,因为它们可以保证任一时刻只有一个线程能访问共享资源,并在其释放锁之前将修改的变量刷新到内存中,
3、有序性
在Java里面,可以通过volatile关键字来保证一定的“有序性”(具体原理在下一节讲述volatile关键字)。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。
Java内存模型:每个线程都有自己的工作内存(类似于前面的高速缓存)。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。
Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。
指令重排序:java语言规范规定JVM线程内部维持顺序化语义。即只要程序的最终结果与它顺序化情况的结果相等,那么指令的执行顺序可以与代码顺序不一致,此过程叫指令的重排序。
指令重排序的意义:JVM能根据处理器特性(CPU多级缓存系统、多核处理器等)适当的对机器指令进行重排序,使机器指令能更符合CPU的执行特性,最大限度的发挥机器性能。
下面就来具体介绍下happens-before原则(先行发生原则):
程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。如果一个线程先去写一个变量,然后一个线程去进行读取,那么写入操作肯定会先行发生于读操作。
传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C。
线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
3、为什么要使用多线程?
- 充分的利用 CPU 和 I/O 的利用率
-
4、创建线程有哪几种方式?new Thread,implement Runnable Callable 线程池
5、什么是守护线程?守护线程是程序运行的时候在后台提供一种通用服务的线程。所有用户线程停止,进程会停掉所有守护线程,退出程序。
Java中把线程设置为守护线程的方法:在 start 线程之前调用线程的 setDaemon(true) 方法。
6、线程的状态有哪几种?怎么流转的?线程在一定条件下,状态会发生变化。线程一共有以下几种状态:
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取 CPU的使用权。即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
(1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒。
(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
(3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
——当线程试图获取某个对象的同步锁时,如果该锁被其他线程所持有,则当前线程进入阻塞状态,如果想从阻塞状态进入就绪状态必须得获取到其他线程所持有的锁。
——当线程调用了一个阻塞式的IO方法时,该线程就会进入阻塞状态,如果想进入就绪状态就必须要等到这个阻塞的IO方法返回。
——当线程调用了某个对象的wait()方法时,也会使线程进入阻塞状态,notify()方法唤醒。
——调用了Thread的sleep(long millis)。线程睡眠时间到了会自动进入阻塞状态。
——一个线程调用了另一个线程的join()方法时,当前线程进入阻塞状态。等新加入的线程运行结束后会结束阻塞状态,进入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
7、线程的优先级有什么用? 当前线程未指定优先级时,所有线程均为普通优先级。
- 优先级从1到10范围指定。10显示最高优先级,1显示最低优先级,5显示普通优先级。优先级最高的线程优先执行。但是,不能保证线程在启动时进入运行状态。
- 与在线程池中等待运行机会的线程相比,运行的线程可能总是有更高的优先级。
- 由调度程序决定执行哪个线程。
- t.setPriority()用于设定线程的优先级。
- 在线程开始方法被调用之前,请记住线程的优先级。
- MIN_PRIORITY、MAX_PRIORITY、NORM_PRIORITY等,设定优先级Java线程的优先级是整数。
https://blog.51cto.com/u_9177933/3002229
8、我们常说的 JUC 是指什么?
不是,违反原子性
9、i++ 是线程安全的吗?
join()方法实现是通过wait()(小提示:Object 提供的方法)。 当main线程调用threadA.join时候,main线程会获得线程对象threadA的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程 (也就是子线程threadA执行完毕退出的时候)
10、join 方法有什么用?什么原理?
Thread.sleep
11、如何让一个线程休眠?
start
12、启动一个线程是用 start 还是 run 方法?
start开辟线程执行,run是在main中执行方法
13、start 和 run 方法有什么区别?
sleep、wait调用后都会暂停当前线程并让出CPU的执行时间,但不同的是sleep不会释放当前持有对象的锁资源,到时间后会继续执行,而wait会释放所有的锁并需要notify/notifyAll后重新获取到对象资源后才能继续执行。
14、sleep 和 wait 方法有什么区别?
Thread. yield( )方法: 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。 cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。
15、Thread.yield 方法有什么用?
yield()方法和sleep()方法类似,也不会释放“锁标志”, 区别在于: 1:sleep方法需要参数,而yield方法不需要参数。 2: sleep()方法给其他线程运行机会时不考虑其他线程的优先级,因此会给低优先级的线程运行的机会;yield()方法只会给相同优先级或更高优先级的线程运行的机会。
16、yield 和 sleep 有什么区别?
17、怎么理解 Java 中的线程中断?
ThreadGroup
18、你怎么理解多线程分组?
wait()
19、你怎么理解 wait、notify、notifyAll?
public final void wait() throws InterruptedException,IllegalMonitorStateException
该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。在调用 wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用 wait()方法。进入 wait()方法后,当前线程释放锁。在从 wait()返回前,线程与其他线程竞争重新获得锁。如果调用 wait()时,没有持有适当的锁,则抛出 IllegalMonitorStateException,它是 RuntimeException 的一个子类,因此,不需要 try-catch 结
notify()
public final native void notify() throws IllegalMonitorStateException
该方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得该对象的对象级别锁,的如果调用 notify()时没有持有适当的锁,也会抛出 IllegalMonitorStateException。
该方法用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则线程规划器任意挑选出其中一个 wait()状态的线程来发出通知,并使它等待获取该对象的对象锁(notify 后,当前线程不会马上释放该对象锁,wait 所在的线程并不能马上获取该对象锁,要等到程序退出 synchronized 代码块后,当前线程才会释放锁,wait所在的线程也才可以获取该对象锁),但不惊动其他同样在等待被该对象notify的线程们。当第一个获得了该对象锁的 wait 线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用 notify 语句,则即便该对象已经空闲,其他 wait 状态等待的线程由于没有得到该对象的通知,会继续阻塞在 wait 状态,直到这个对象发出一个 notify 或 notifyAll。这里需要注意:它们等待的是被 notify 或 notifyAll,而不是锁。这与下面的 notifyAll()方法执行后的情况不同。
notifyAll()
public final native void notifyAll() throws IllegalMonitorStateException
该方法与 notify ()方法的工作方式相同,重要的一点差异是:
notifyAll 使所有原来在该对象上 wait 的线程统统退出 wait 的状态(即全部被唤醒,不再等待 notify 或 notifyAll,但由于此时还没有获取到该对象锁,因此还不能继续往下执行),变成等待获取该对象上的锁,一旦该对象锁被释放(notifyAll 线程退出调用了 notifyAll 的 synchronized 代码块的时候),他们就会去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出 synchronized 代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。
20、同步和异步的区别?
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去; 异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。 当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
21、什么是死锁?
死锁描述的是两个或多个线程由于相互等待而永远被阻塞的情况。导致死锁的原因有很多。线程分析器可检测由于不恰当使用互斥锁而导致的死锁。此类死锁通常发生在多线程应用程序中。
满足以下所有条件时,包含两个或多个线程的进程可能会进入死锁状态:
已持有锁的线程请求新锁
同时发出对新锁的请求
两个或多个线程形成一个循环链,其中每个线程都在等待链中下一个线程持有的锁
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(3) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
22、怎么避免死锁?
23、什么是活锁?
活锁指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程。 处于活锁的实体是在不断的改变状态,活锁有可能自行解开。
24、什么是无锁?
什么是CAS无锁机制?
CAS:Compare and Swap,即比较再交换。
jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。
CAS算法理解:
(1)与锁相比,使用比较交换(下文简称CAS)会使程序看起来更加复杂一些。但由于其非阻塞性,它对死锁问题天生免疫,并且,线程间的相互影响也远远比基于锁的方式要小。更为重要的是,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销,因此,它要比基于锁的方式拥有更优越的性能。
(2)无锁的好处:
第一,在高并发的情况下,它比有锁的程序拥有更好的性能;
第二,它天生就是死锁免疫的。
就凭借这两个优势,就值得我们冒险尝试使用无锁的并发。
(3)CAS算法的过程是这样:它包含三个参数CAS(V,E,N): V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。
(4)CAS操作是抱着乐观的态度进行的,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
(5)简单地说,CAS需要你额外给出一个期望值,也就是你认为这个变量现在应该是什么样子的。如果变量不是你想象的那样,那说明它已经被别人修改过了。你就重新读取,再次尝试修改就好了。
(6)在硬件层面,大部分的现代处理器都已经支持原子化的CAS指令。在JDK 5.0以后,虚拟机便可以使用这个指令来实现并发操作和并发数据结构,并且,这种操作在虚拟机中可以说是无处不在。
CAS无锁机制 三个参数
V :需要更新的变量 主内存中的
E :预期值 本地内存的
N :新值
如果:
V=E 说明没有被修改过 将V的值设为N
V!=E 说明被修改过 ,说明已经有其他线程做了更新,则当前线程重新计算,返回计算后的值
CAS比较与交换的伪代码可以表示为
do{
备份旧数据;
基于旧数据构造新数据;
}while(!CAS( 内存地址,备份的旧数据,新数据 ))
就是指当两者进行比较时,如果相等,则证明共享数据没有被修改,替换成新值,然后继续往下运行;如果不相等,说明共享数据已经被修改,放弃已经所做的操作,然后重新执行刚才的操作。容易看出 CAS 操作是基于共享数据不会被修改的假设,采用了类似于数据库的 commit-retry 的模式。当同步冲突出现的机会很少时,这种假设能带来较大的性能提升。
25、什么是线程饥饿?
26、什么是 CAS?
27、阻塞和非阻塞的区别?
28、并发和并行的区别?
29、为什么不推荐使用 stop 停止线程?
30、如何优雅地终止一个线程?
31、Synchronized 同步锁有哪几种用法?
32、什么是重入锁(ReentrantLock)?
33、Synchronized 与 ReentrantLock 的区别?
34、synchronized 锁的是什么?
35、什么是读写锁?
36、公平锁和非公平锁的区别?
37、有哪些锁优化的方式?
38、什么是偏向锁?
39、什么是轻量级锁?
40、什么是自旋锁?
41、什么是锁消除?
42、什么是锁粗化?
43、什么是重量级锁?
44、什么是线程池?
45、使用线程池有什么好处?
46、创建一个线程池有哪些核心参数?
47、线程池的工作流程是怎样的?
48、Java 里面有哪些内置的线程池?
49、为什么阿里不让用 Executors 创建线程池?
50、线程池的拒绝策略有哪几种?
51、如何提交一个线程到线程池?
52、线程池 submit 和 execute 有什么区别?
53、如何查看线程池的运行状态?
54、如何设置线程池的大小?
55、如何关闭线程池?
56、AQS 是什么?
57、AQS 的底层原理是什么?
58、Java 中的 Fork Join 框架有什么用?
59、ThreadLocal 有什么用?
60、ThreadLocal 有什么副作用?
61、volatile 关键字有什么用?
62、volatile 有哪些应用场景?
63、CyclicBarrier 有什么用?
64、CountDownLatch 有什么用?
65、CountDownLatch 与 CyclicBarrier 的区别?
66、Semaphore 有什么用?
67、Exchanger 有什么用?
68、LockSupport 有什么用?
69、Java 中原子操作的类有哪些?
70、什么是 ABA 问题?怎么解决?
71、Java 并发容器,你知道几个?
72、什么是阻塞队列?
73、阻塞队列有哪些常用的应用场景?
74、Java 中的阻塞的队列有哪些?
75、什么是幂等性