关于多线程中的面试题

常见面试的

1.现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
答:使用join就OK了。

public class ThreeThread {public static void main(String[] args) throws InterruptedException {OneThread one =new OneThread();one.setName("one");one.start();one.join();  //进入等待,只有one线程执行完毕,才会释放等待,才能继续往下执行。OneThread two =new OneThread();two.setName("two");two.start();two.join();   //join()这一句,就好比:thread.sleep();一样,不过比sleep好很多。OneThread three =new OneThread();three.setName("three");three.start();}
}class OneThread extends Thread {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName());}
}

解答:join可以暂时理解为wait,或者sleep。

join源码分析:


public final void join() throws InterruptedException {join(0);} public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {//isAlive()是一个本地方法,判断该线程是否存活,如果该线程还存活着,则一直等待0秒。while (isAlive()) {  wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

可以看到join其实就是利用了wait()方法来实现的。当调用one.join()时,main()主线程被wait了,就不能继续往下执行,也就不能执行two.start()。
只有等到one线程死亡,也就是执行完毕后,wait才会被释放,继续往下执行。

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

进程用于把资源集中到一起,而线程则是在CPU上被调度执行的实体。

进程:指在系统中能独立运行并作为资源分配的基本单位,它是由一组机器指令、数据和堆栈等组成的,是一个能独立运行的活动实体。

线程:是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源。

多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响. 线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。

3.线程上下文切换是什么?

即使是单核CPU也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停地切换线程执行,让我们感觉多个线程时同时执行的,时间片一般是几十毫秒(ms)。

CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再次加载这个任务的状态,从任务保存到再加载的过程就是一次上下文切换。

这就像我们同时读两本书,当我们在读一本英文的技术书籍时,发现某个单词不认识,于是便打开中英文词典,但是在放下英文书籍之前,大脑必须先记住这本书读到了多少页的第多少行,等查完单词之后,能够继续读这本书。这样的切换是会影响读书效率的,同样上下文切换也会影响多线程的执行速度。

更多信息查看我的文章:《java多线程-进程与线程(一)》

4.在java中wait和sleep方法的不同?
最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。
wait线程会被挂起,让出当前cpu调度以及资源,sleep则会根据sleep时间一直占用者cpu以及资源。

更多信息查看我的文章:《java多线程-线程间通信(七)》

5.为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

线程中断机制

手动中断

public class Handinterrupt{public volatile boolean b=true;public void run(){while(b){  //通过外面b变量来中断.... }}
}

interrupt中断

class MyThreadStop extends Thread {private int temp;public MyThreadStop(int temp) {this.temp = temp;}@Overridepublic void run() {for (int i = 0; i < 5; i++) { System.out.println("i=" + i);if (Thread.interrupted()) { //接受停止的状态System.out.println("线程已经停止了哦");break;}}System.out.println(Thread.currentThread().getName() + temp);}}public class ThreadStopDome {public static void main(String orgs[]) {MyThreadStop myThread = new MyThreadStop(1);myThread.start();myThread.interrupt();//发起停止线程System.out.println("运行完毕");}}

interrupt并不能真正的停止线程,只是将线程的状态标记为停止;需要配合Thread.interrupted()使用,这个读取线程的状态后,发现线程被停止了, 然后做一些相关操作。

lockInterruptibly中断


public class ReentrantDome4 {private Lock lock = new ReentrantLock();public void serviceA() {try { //可中断,响应中断。这个和lock是一样的,只不过在lock()的基础上加了一个判断,用来响应线程的中断。lock.lockInterruptibly();Thread.sleep(500);System.out.println("lock了。。。。。" + Thread.currentThread().getId());} catch (InterruptedException e) {e.printStackTrace();System.out.println("中断了.....");} finally {lock.unlock();}}public static void main(String[] args) {final ReentrantDome4 dome4 = new ReentrantDome4();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("threadB=" + Thread.currentThread().getId());dome4.serviceA();}});t2.start();t2.interrupt(); //如果把这个去了,相对来说lock.lockInterruptibly()就和lock.lock()功能一样了。}}

lock.lockInterruptibly();加锁时判断线程是否被停止了。如果线程停止了,直接抛异常,不往下执行了。

t2.start(); //开始线程
t2.interrupt(); //停止线程

如果start已经开始了在到达lock.lockInterrup 时执行了interrupt线程才可能停止,如果已经执行到lock.locIn之后的语句了,也就无法停止了。lock.lockInterruptibly()只是在获取锁时,判断是否该线程已经停止了, 如果停止了则直接抛异常,不往下执行了。如果已经获取到锁,并且执行了,这个时候才停止,则停止是无效的。

源码:

public final void acquireInterruptibly(int arg)throws InterruptedException {if (Thread.interrupted())  //判断是否停止了throw new InterruptedException();if (!tryAcquire(arg))doAcquireInterruptibly(arg);}public void interrupt() {if (this != Thread.currentThread())checkAccess();synchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupt0();           // Just to set the interrupt flagb.interrupt(this);return;}}interrupt0();}

多线程中的线程中断

ExecutorService pool = Executors.newFixedThreadPool(10);
pool.shutdown();  //停止线程池。线程池停止了,就是说线程池里面的线程也应该停止了。

源码:

public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();advanceRunState(SHUTDOWN);interruptIdleWorkers(); //停止线程onShutdown(); // hook for ScheduledThreadPoolExecutor} finally {mainLock.unlock();}tryTerminate();
}private void interruptIdleWorkers(boolean onlyOne) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {for (Worker w : workers) {Thread t = w.thread;//尝试获取锁,并且线程被停止了,开始执行t.interruptif (!t.isInterrupted() && w.tryLock()) {try {t.interrupt();} catch (SecurityException ignore) {} finally {w.unlock();}}if (onlyOne)break;}} finally {mainLock.unlock();}}

总结:

Java中,明确规定任何线程都不可能停止另外一个正在运行的线程。

上面我们可以看到,他并不是真正的停止了,只是根据接受到的状态,做响应的操作而已,而不是真正意义上的线程停止了。可以看到lock的可停止线程,内部原理也是使用的interrupt来操作的。

死锁问题

写一个简单死锁程序,或者怎么解决死锁,已经死锁的原因?
死锁一般发送在至少两把锁以上被多个线程使用而导致的,或者使用lock忘记锁解锁。

看一个synchronized死锁的问题:


class DeadThread{private final Object left = new Object();private final Object right = new Object();public void leftRight() throws Exception{synchronized (left){System.out.println("left...start");Thread.sleep(2000);synchronized (right){System.out.println("leftRight end!");}System.out.println("left end");}}public void rightLeft() throws Exception{synchronized (right){System.out.println("right...start");Thread.sleep(2000);synchronized (left){System.out.println("rightLeft end!");}System.out.println("right....end");}}
}public class DeadLock2 {public static void main(String[] args) {final DeadThread thread=new DeadThread();Thread thread1= new Thread(new Runnable() {@Overridepublic void run() {try {thread.leftRight();} catch (Exception e) {e.printStackTrace();}}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {try {thread.rightLeft();} catch (Exception e) {e.printStackTrace();}}});thread1.start();thread2.start();}
}

上面发生死锁了,为什么?因为线程1,2他们几乎一起执行,线程1执行了leftRight方法,刚开始获取到了left对象锁;线程2执行了rightLeft方法,刚进入就获取到了right对象锁。当等待2s过后,线程1需要right这把锁,但是right这把锁在线程2手里,线程2s后需要left这把锁,结果left锁在线程1里面,这样就产生了死锁。

来看下使用Lock的tryLock锁避免死锁。


class LockThread {private Lock lock = new ReentrantLock();private Lock lock2 = new ReentrantLock();void leftRight() throws Exception {try {lock.lock();System.out.println("left...start");Thread.sleep(2000);boolean a = lock2.tryLock();System.out.println("left...a=" + a);System.out.println("left end!");} finally {lock.unlock();lock2.unlock();}}void rightLeft() throws Exception {try {lock2.lock();System.out.println("right...start");Thread.sleep(2000);boolean a = lock.tryLock();System.out.println("right...a=" + a);System.out.println("right end!");} finally {lock2.unlock();lock.unlock();}}
}public class DeadLock3 {public static void main(String[] args) {final LockThread thread = new LockThread();Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {try {thread.leftRight();} catch (Exception e) {e.printStackTrace();}}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {try {thread.rightLeft();} catch (Exception e) {e.printStackTrace();}}});thread1.start();thread2.start();}
}结果为:
left...start
right...start
right...a=false
right end!
left...a=false
left end!
java.lang.IllegalMonitorStateExceptionat java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)at cn.thread.first.dielock.LockThread.rightLeft(DeadLock3.java:35)at cn.thread.first.dielock.DeadLock3$2.run(DeadLock3.java:61)at java.lang.Thread.run(Thread.java:745)
java.lang.IllegalMonitorStateExceptionat java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:460)at cn.thread.first.dielock.LockThread.leftRight(DeadLock3.java:21)at cn.thread.first.dielock.DeadLock3$1.run(DeadLock3.java:50)at java.lang.Thread.run(Thread.java:745)

可以看到tryLock在尝试过程中没有获取到锁,就不加锁,所以在finally里面,解锁就会抛异常了。

tryLock只是尝试看看自己能否获取锁,能获取就获取锁并返回true,不能获取就直接返回false不做任何操作,没有获取锁的时候并不会把线程加入到等待队列里面(相当于没有锁,不加锁),tryLock只是在能获取锁的时候获取锁。

在多把锁为防止死锁可以尝试使用tryLock来操作,或者使用boolean a = lock2.tryLock(1, TimeUnit.SECONDS);在1s钟内一直尝试获取锁,如果1s后还不能获取则返回false。这样就可以很好的解决死锁带来的问题。

用Java写代码来解决生产者——消费者问题


import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class ProducerAndCosumer<T> {private static final Integer MAX_ITEMS = 10;private LinkedList<T> items = new LinkedList<>();private Lock lock = new ReentrantLock();private Condition full = lock.newCondition();  //元素满的时候,生产者进入等候private Condition empty = lock.newCondition();  //元素为空的时候,消费者进入等候public T put(T o) throws InterruptedException {lock.lock();try {while (items.size() == MAX_ITEMS) {full.await();}insert(o);} finally {lock.unlock();}return o;}private void insert(T o) {items.add(o);empty.signal();  //解锁消费者等候}public T get() throws InterruptedException {lock.lock();try {if (items.isEmpty()) {empty.await();}full.signal();  //解锁生产者等候return items.removeFirst();} finally {lock.unlock();}}}public class ProducerAndCosumerDemo {public static void main(String[] args) {final ProducerAndCosumer<Integer> cosumer = new ProducerAndCosumer<>();Runnable producerRunnable = new Runnable() {int i = 0;public void run() {while (true) {i++;try {System.out.println("我生产了一个===" + i);cosumer.put(i);Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}};Runnable cosumerRunnable = new Runnable() {public void run() {while (true) {try {System.out.println("消费了一个===" + cosumer.get());Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}};Thread thread = new Thread(producerRunnable);thread.start();Thread thread1 = new Thread(cosumerRunnable);thread1.start();}}

队列满了,那么生产者就开始等候;队列空了,消费着就开始等候;队列有值了,就解锁消费者等候;消费掉一条数据时就解锁生产者等候。

可以参考ArrayBlockingQueue源码实现。

Java中的volatile关键是什么作用?怎样使用它?在Java中它跟synchronized方法有什么不同?

volatile赋予了变量可见——禁止编译器对成员变量进行优化,它修饰的成员变量在每次被线程访问时,都强迫从内存中重读该成员变量的值;而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存,这样在任何时刻两个不同线程总是看到某一成员变量的同一个值,这就是保证了可见性

可参看《java多线程-volatile》与《java多线程-synchrionized》

关于多线程中的面试题相关推荐

  1. java中的多线程有什么意义_Java多线程与并发面试题(小结)

    1,什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器编程,你可以使用多线程对运算密集型任务提速.比如,如果一个线程完成一 ...

  2. 多线程原理分析面试题理解

    系列前言 本系列是本人参加微软亚洲研究院,腾讯研究院,迅雷面试时整理的,另外也加入一些其它IT公司如百度,阿里巴巴的笔试面试题目,因此具有很强的针对性.系列中不但会详细讲解多线程同步互斥的各种&quo ...

  3. java run里面定义变量_Java程序员50多道最热门的多线程和并发面试题(答案解析)...

    下面是Java程序员相关的热门面试题,你可以用它来好好准备面试. 1) 什么是线程? 线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位.程序员可以通过它进行多处理器 ...

  4. 多线程中的事务回滚,你真的用对了吗?

    点击关注公众号,实用技术文章及时了解 来源:blog.csdn.net/weixin_43225491/article/ details/117705686 背景介绍 1,最近有一个大数据量插入的操作 ...

  5. java 面试题 生产者 消费者_面试大厂必看!就凭借这份Java多线程和并发面试题,我拿到了字节和美团的offer!...

    最近好多粉丝私信我说在最近的面试中老是被问到多线程和高并发的问题,又对这一块不是很了解,很简单就被面试官给问倒了,被问倒的后果当然就是被刷下去了,因为粉丝要求,我最近也是花了两天时间 给大家整理了这一 ...

  6. 多线程中Thread的join方法

    多线程中Thread的join方法 join简介 join方法是Thread类中的一个方法,该方法的定义是等待该线程执行直到终止.其实就说join方法将挂起调用线程的执行,直到被调用的对象完成它的执行 ...

  7. 【Linux】多线程中使用fork()

    (最核心的东西我在下面用红色字体标出来了,理解了那块,这些东西都是就理解了!) 在本篇文章开始之前,需要大家先了解线程和进程,这位大哥讲的言简意赅:进程和线程的主要区别(总结)_kuangsongha ...

  8. java闭合数据_java多线程中线程封闭详解

    线程封闭的概念 访问共享变量时,通常要使用同步,所以避免使用同步的方法就是减少共享数据的使用,这种技术就是线程封闭. 实现线程封闭的方法 1:ad-hoc线程封闭 这是完全靠实现者控制的线程封闭,他的 ...

  9. java多线程中的join方法详解

    java多线程中的join方法详解 方法Join是干啥用的? 简单回答,同步,如何同步? 怎么实现的? 下面将逐个回答. 自从接触Java多线程,一直对Join理解不了.JDK是这样说的:join p ...

最新文章

  1. Google更新机器学习开发套件ML Kit,新增支持自动回复与语言识别
  2. C++中的friend详细解析
  3. 网工路由基础(8)路由重分布
  4. 【JZOJ3636】【BOI2012】Mobile(mobile)
  5. CXF配置,ant文件说明及运行,运行cxf中带的项目
  6. 转:V.I. Arnold 论数学教育
  7. C#多线程学习之:Monitor类
  8. [Swift]LeetCode85. 最大矩形 | Maximal Rectangle
  9. Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇)
  10. LR 报错误: C interpreter run time error: Error -- Unresolved symbol : vuser_end解决方法
  11. LINUX编译opencv
  12. vision应用教程中文版
  13. 黑马程序员python全套视频-python视频教程免费:求黑马程序员python教程?
  14. property_get和property_set使用方法
  15. ftp服务器文件编码类型,ftp服务器编码格式
  16. ie打开本地html页面慢,ie11 第一次浏览jquery+CSS3网页时候延时3秒
  17. python摄像头人脸识别小程序 开门,OpenCV3-Python人脸识别方法—基于摄像头
  18. sed编辑器之修改行
  19. mysql事务排队情况_MySQL事务问题
  20. Clipper库 | 类型和填充规则

热门文章

  1. Ubuntu 14.04.5安装搜狗输入法
  2. Java判断Long类型相等问题
  3. android ocr 中文版,android ocr
  4. sql 转换 不足位数补0
  5. Python+Opencv实现图像匹配——模板匹配
  6. 【mac】MacBook使用快捷键
  7. NLP预训练模型综述
  8. CodeForces - 777D Cloud of Hashtags
  9. KEIL仿真 logic analyzer
  10. gammaray报Error: gdb: Yama security extension is blocking runtime attaching, see /proc/sys/kernel/yam