14日面试题:
问题1:线程类的构造方法,静态代码块是被哪个线程调用的?
解析:
首先提到了线程类,那么在java中创建(实现)线程是有三种方式的:
实现interface Runnable的run() 继承class Thread override run() 使用FutureTask的方式(此方式有返回值,但是实际上还是要依赖于Thread类)
补充FutureTask方式:
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) { FutureTask<String> futureTask = new FutureTask<>(new CallerTask()); new Thread(futureTask).start(); try { String result = futureTask.get(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } } class CallerTask implements Callable<String> { @Override public String call() throws Exception { return "i am a baby"; } }
综合上面三种方式,结合问题的构造方法,那么答案应该就是Thread的构造方法了。
提到静态代码块:先找到静态代码块的概念:
在Java中使用static和{}声明的代码块就是静态代码块。
查了半天也没找到很明确的指向:看到群友说百度到了:静态代码块是被new这个线程类所在线程所调用的。
静态代码块是在类加载的时候调用的,且类加载器是缺省规则,那么调用静态代码块的线程就是调用当前类加载器的线程 参考文档
问题2:同步方法和同步代码块,哪个是更好的选择?
解析:
同步方法:在方法上加入关键字:synchronized的方法
同步代码块:在代码块上加入关键字:synchronized(Object){} (小括号内即为加锁的对象)
选用同步代码块好,因为可以只将可能出现并发问题的代码放入同步代码块中,且同步代码块可以自行选择加锁的对象。同步方法的锁对象是当前类的实例
问题3:如何检测死锁,怎么预防死锁?
解析:
死锁:两个或者两个以上的线程在执行过程中,因为争夺资源而造成的互相等待的现象。(锁已经没有人拿了,但是现在所有的线程都在等待拿锁,而不是去拿锁)
死锁的四个必要条件:
互斥条件:资源只能被一个线程占用,若资源已经被占用,其他线程需要等待知道占用线程释放资源。
请求并持有条件:一个线程已经占用至少一个资源,又提出新的资源占用,但是新资源被其他线程占用,所以当前线程被阻塞,且不会释放已经占用的资源。
不可剥夺条件:线程占用资源后,在使用完之前是不会释放的,且不可能被其他线程夺走。
环路等待条件:发生死锁时,必然存在一个线程——资源的环形链。(请求并持有条件串成环既是)。
避免死锁需要破坏至少一个死锁的必要条件即可,实际上只有请求并持有和环路等待是可以破坏的。
那么如何破坏请求并持有条件和环路条件呢?实际上通过资源申请的有序性就可以实现。(反过来理解也可以认为是因为资源申请的有序性无法得到保障才导致的请求并持有和环路等待)