美国上市公司,专注Java培训22年

JavaSE面试题(十二):多线程(2)


Q:多线程的创建方式?

方式一:继承Thread类创建线程类

1 class T extends Thread{2 @Override3 public void run() {45 }6 }

方式二:通过Runnable接口创建线程类

1 class T implements Runnable{2 @Override3 public void run() {45 }6 }

方式三:通过Callable和Future创建线程

1 class T implements Callable<String> {2 @Override3 public String call() throws Exception {4 return null;5 }6 }

Q:启动一个线程是调用 run() 方法还是 start() 方法?

启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。

run()方法是线程启动后要进行回调(callback)的方法。

Q:什么情况下导致线程死锁,遇到线程死锁该怎么解决?

死锁的定义:

死锁是指多个线程因竞争资源而造成的一种互相等待状态,若无外力作用,这些进程都将无法向前推进。

死锁的条件:

互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个线程

所占有。此时若有其他线程请求该资源,则请求线程只能等待。

不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。

请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合{Pl, P2, …, pn},其中 Pi 等待的资源被 P(i+1)占有(i=0, 1, …, n-1),Pn 等待的资源被 P0 占有

循环等待.png

避免死锁:

①加锁顺序(线程按照一定的顺序加锁)

②加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)

Q:什么是乐观锁和悲观锁?

悲观锁

Java在JDK1.5之前都是靠synchronized关键字保证同步的,这种通过使用一致的锁定协议来协调对共享状态的访问,可以确保无论哪个线程持有共享变量的锁,都采用独占的方式来访问这些变量。独占锁其实就是一种悲观锁,所以可以说synchronized是悲观锁。

乐观锁

乐观锁(Optimistic Locking)其实是一种思想。相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

Q:乐观锁一定就是好的吗?

乐观锁避免了悲观锁独占对象的现象,同时也提高了并发性能,但它也有缺点:

1.乐观锁只能保证一个共享变量的原子操作。如果多一个或几个变量,乐观锁将变得力不从心,

但互斥锁能轻易解决,不管对象数量多少及对象颗粒度大小。

2.长时间自旋可能导致开销大。假如CAS长时间不成功而一直自旋,会给CPU带来很大的开销。

3.ABA问题。CAS的核心思想是通过比对内存值与预期值是否一样而判断内存值是否被改过,但这个判断逻辑不严谨,假如内存值原来是A,后来被一条线程改为B,最后又被改成了A,则CAS认为此内存值并没有发生改变,但实际上是有被其他线程改过的,这种情况对依赖过程值的情景的运算结果影响很大。

解决的思路是引入版本号,每次变量更新都把版本号加一。

Q:说一下线程池的启动策略?

线程池的执行过程描述:

1/* 2* Proceed in 3 steps: 3* 4* 1. If fewer than corePoolSize threads are running, try to 5* start a new thread with the given command as its first 6* task. The call to addWorker atomically checks runState and 7* workerCount, and so prevents false alarms that would add 8* threads when it shouldn't, by returning false. 9*10* 2. If a task can be successfully queued, then we still eed11* to double-check whether we should have added a thread12* (because existing ones died since last checking) or that13* the pool shut down since entry into this method. So we14* recheck state and if necessary roll back the enqueuing if15* stopped, or start a new thread if there are none.16*17* 3. If we cannot queue task, then we try to add a new18* thread. If it fails, we know we are shut down or saturated19* and so reject the task.20*/

线程池的执行过程.png

1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。

2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:

①如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

②如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。

③如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这

个任务;

④如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告

诉调用者“我不能再接受任务了”。

3、当一个线程完成任务时,它会从队列中取下一个任务来执行。

4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

Q:请说出同步线程及线程调度相关的方法?

wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理 InterruptedException 异常;

notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且与优先级无关;

notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

注意:java 5 通过 Lock 接口提供了显示的锁机制,Lock 接口中定义了加锁(lock()方法)和解锁(unLock()方法),增强了多线程编程的灵活性及对线程的协调

Q:线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?

不是。线程池默认初始化后不启动 Worker,等待有请求时才启动。

当调用 execute方法添加一个任务时,线程池会做如下判断:

如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;

如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;

如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会抛出异常RejectExecutionException当一个线程完成任务时,它会从队列中取下一个任务来执行。

当一个线程无事可做,超过一定的时间(keepAlive)时,线程池会判断。

如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize的大小。

免责声明:内容来源于公开网络,若涉及侵权联系尽快删除!


【免责声明】本文部分系转载,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,如涉及作品内容、版权和其它问题,请在30日内与我们联系,我们会予以重改或删除相关文章,以保证您的权益!

Java开发高端课程免费试学

大咖讲师+项目实战全面提升你的职场竞争力

  • 海量实战教程
  • 1V1答疑解惑
  • 行业动态分析
  • 大神学习路径图

相关推荐

更多
  • eclipse怎么建立Java项目?
    eclipse怎么建立Java项目?
    eclipse怎么建立Java项目?在使用Eclipse建立Java项目时,可以遵循以下步骤: 详情>>

    2024-05-10

  • Java关键词汇总-Java关键字有哪些及其作用?
    Java关键词汇总-Java关键字有哪些及其作用?
    Java关键词汇总-Java关键字有哪些及其作用?Java语言中的关键字(Keywords)是Java编程语言的核心组成部分,它们具有特定的意义,并被用于表示基本数据类型、控制语句、访问权限等。Java关键字总数大约有50个左右,下面列出了一些常用的Java关键字及其作用: 详情>>

    2024-04-08

  • Java编程工具用哪个最好
    Java编程工具用哪个最好
    Java编程工具用哪个最好?Java编程工具的选择取决于个人的偏好、项目需求和开发环境。以下是一些常用的Java编程工具: 详情>>

    2024-04-03

  • 好用的Java编写软件
    好用的Java编写软件
    Java是一种广泛使用的编程语言,因其跨平台、安全性和稳定性而受到许多开发者的喜爱。Java被用于开发各种类型的软件,从桌面应用程序到企业级服务器端应用程序。以下是一些常见的、用Java编写的软件示例: 详情>>

    2024-04-02

  • Java开班时间

    收起