1、说说进程,线程,协程之间的区别
进程是程序运行和资源分配的基本单位,一个程序至少一个进程,一个进程至少一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高,线程是进程的一个实体,是 CPU 调度和分派的基本单位。
2、什么是守护线程?它和非守护线程的区别是?
程序运行完毕,JVM 会等待非守护线程完成后关闭,但是 JVM 不会等待守护线程。典型的例子就是 GC 线程。
3、什么是多线程上下文切换?
多线程的上下文切换是指 CPU 由一个正在运行的线程切换到另外一个等待就绪的线程获取执行权限的过程,
4、线程创建的方式?继承 Thread 类创建线程类
继承 Thread 类,重写 Run() 方法。
创建 Thread 子类的实例,即创建了线程对象。
调用 Thread 对象的 start() 方法来启动该线程。
通过 Runnable 接口创建线程类
创建 Runnable 接口的实现类,重写 Run() 方法。
创建 Runnable 实现类的实例,并依此实例作为 Thread 的 target 来创建 Thread 对象,该 Thread 对象才是真正的线程对象。
通过 Thread 对象的 start() 方法启动线程。
5、Thread 类中的 start() 和 run() 方法有什么区别?
start() 方法用来创建新线程,start() 方法内部调用了 run() 方法,这和直接调用 run() 方法的效果不一样。当你调用 run() 方法的时候,只会在原来的线程中调用,没有新的线程启动。
6、怎么检测一个线程是否持有对象监视器?
Thread 类提供了一个 holdsLock() 方法,该方法是一个静态方法,当对象 obj 的监视器被当前线程持有的时候才会返回 true
7、Runnable 和 Callable 的区别?
Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;
Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以方便获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务
8、sleep()、join()、yield()有什么区别
sleep():
暂停当前线程,按照给予时间进入阻塞状态,让其他线程继续执行。当时该方法不会释放锁标记,加入使用了 synchronized 同步块,其他线程依然无法访问共享数据。
wait():
wait() 方法需要和 notify() 及 notifyAll() 两个方法一起介绍,这三个方法用于协调多个线程对共享数据的存取,所以必须在 synchronized 语句块内使用,也就是说,调用 wait(),notify() 和 notifyAll() 的任务在调用这些方法前必须拥有对象的锁。注意,它们都是 Object 类的方法,而不是 Thread 类的方法。
wait() 方法与 sleep() 方法的不同之处在于,wait() 方法会释放对象的“锁标志”。当调用某一对象的 wait() 方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了 notify() 方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的 notifyAll() 方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。
除了使用 notify() 和 notifyAll() 方法,还可以使用带毫秒参数的 wait(long timeout) 方法,效果是在延迟 timeout 毫秒后,被暂停的线程将被恢复到锁标志等待池。
此外,wait(),notify() 及 notifyAll() 只能在 synchronized 语句中使用,但是如果使用的是 ReenTrantLock 实现同步,该如何达到这三个方法的效果呢?解决方法是使用 ReenTrantLock.newCondition() 获取一个 Condition 类对象,然后 Condition 的 await(),signal() 以及 signalAll() 分别对应上面的三个方法。
yield():
yield() 方法和 sleep() 方法类似,也不会释放“锁标志”,区别在于,它没有参数,即 yield() 方法只是使当前线程重新回到可执行状态,所以执行 yield() 的线程有可能在进入到可执行状态后马上又被执行,另外 yield() 方法只能使同优先级或者高优先级的线程得到执行机会,这也和 sleep() 方法不同。
join():
join() 方法会使当前线程等待调用 join() 方法的线程结束后才能继续执行
9、产生死锁的条件
1.互斥条件:一个资源每次只能被一个进程使用。 2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。 4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。