java面试题总结系列(多线程)

什么是进程,什么是线程?

进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。

线程包含在进程当中,是进程中实际运行单位。 

 

java实现多线程的几种方式,应该使用哪种方式比较好?

  (1)继承 java.lang.Thread 类

  (2)实现 java.lang.Runnable 接口

  如果你要继承其他类,最好实现Runnable。

 

Thread类中的Start() 和run() 方法的区别?

start()方法是用来启动新创建的线程,而start()内部调用的run(),如果直接调用run(),则是在原来的线程中运行的。

 

Runnable和Callable有什么不同?
Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。

Runnable可以作为Thread构造器的参数,通过开启新的线程来执行,也可以通过线程池来执行。而Callable只能通过提交给线程池ExecutorService执行。

 

CountDownLatch、CyclicBarrier和Semaphore

  1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

    CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

    而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

    另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

  2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

 

为什么wait, notify 和 notifyAll这些方法不在thread类里面?

JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。

 

谈谈你对乐观锁和悲观锁的区别

悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。

乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号,CAS等机制来实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

 

谈谈你对ThreadLocal的理解

ThreadLocal是线程的局部变量,是每一个线程所单独持有的,其他线程不能访问。

 

关于ThreadLocalMap<ThreadLocal, Object>弱引用问题

当线程没有结束,但是ThreadLocal已经被回收,则可能导致线程中存在ThreadLocalMap<null, Object>的键值对,造成内存泄露。(ThreadLocal被回收,ThreadLocal关联的线程共享变量还存在)。

虽然ThreadLocal的get,set方法可以清除ThreadLocalMap中key为null的value,但是get,set方法在内存泄露后并不会必然调用,所以为了防止此类情况的出现,我们有两种手段。

1、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;

2、JDK建议ThreadLocal定义为private static,这样ThreadLocal的弱引用问题则不存在了。

 

volatile关键字的作用 

1.保证此变量对所有的线程的可见性

2.禁止指令重排序优化

volatile 性能:volatile 的读性能消耗与普通变量几乎相同,但是写操作稍慢,因为它需要在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行。

 

线程池的原理,为什么要创建线程池?

先启动若干数量的线程,并让这些线程都处于睡眠状态,当客户端有一个新请求时,就会唤醒线程池里的某一个睡眠线程,让他处理这个请求,当处理完这个请求后,线程又处于睡眠状态。

为每个请求创建一个新线程的开销很大;为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个 JVM 里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。线程池为线程生命周期开销问题和资源不足问题提供了解决方案

 

线程的生命周期,什么时候会出现僵死进程;

新建(new Thread)

当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。例如:Thread  t1=new Thread();

就绪(runnable)

调用Thread类的start方法,线程已经被启动,进入就绪状态,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。

运行(running)

线程获得CPU资源正在执行任务(执行run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束或者时间片结束。

堵塞(blocked)

由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。阻塞结束后线程进入就绪状态。

死亡(dead)

当线程执行完毕(run方法运行结束)或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。

 

posted @ 2019-04-19 14:03  Zuul  阅读(203)  评论(0编辑  收藏  举报