Java程序员面试中的多线程问题-程序员宅基地

技术标签: 面试  java  数据库  

很多核心Java面试题来源于多线程(Multi-Threading)和集合框架(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的。这篇文章收集了 Java 线程方面一些典型的问题,这些问题经常被高级工程师所问到。

0.Java 中多线程同步是什么?

在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个 Java 线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果。

1.解释实现多线程的几种方法?

一 Java 线程可以实现 Runnable 接口或者继承 Thread 类来实现,当你打算多重继承时,优先选择实现 Runnable。

2.Thread.start ()与 Thread.run ()有什么区别?

Thread.start ()方法(native)启动线程,使之进入就绪状态,当 cpu 分配时间该线程时,由 JVM 调度执行 run ()方法。

3.为什么需要 run ()和 start ()方法,我们可以只用 run ()方法来完成任务吗?

我们需要 run ()&start ()这两个方法是因为 JVM 创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的 start 方法来完成,start 由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了 Runnable 接口,这就避免因继承了 Thread 类而造成的 Java 的多继承问题。

4.什么是 ThreadLocal 类,怎么使用它?

ThreadLocal 是一个线程级别的局部变量,并非“本地线程”。ThreadLocal 为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本(译者注)。

下面是线程局部变量(ThreadLocal variables)的关键点:

一个线程局部变量(ThreadLocal variables)为每个线程方便地提供了一个单独的变量。

ThreadLocal 实例通常作为静态的私有的(private static)字段出现在一个类中,这个类用来关联一个线程。

当多个线程访问 ThreadLocal 实例时,每个线程维护 ThreadLocal 提供的独立的变量副本。

常用的使用可在 DAO 模式中见到,当 DAO 类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)

ThreadLocal 难于理解,下面这些引用连接有助于你更好的理解它。

Good article on ThreadLocal on IBM DeveloperWorks 》、《理解 ThreadLocal》、《Managing data : Good example》、《Refer Java API Docs

5.什么时候抛出 InvalidMonitorStateException 异常,为什么?

调用 wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,那么就会抛出 IllegalMonitorStateException 的异常(也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试调用 wait ()/notify ()/notifyAll ()时)。由于该异常是 RuntimeExcpetion 的子类,所以该异常不一定要捕获(尽管你可以捕获只要你愿意).作为 RuntimeException,此类异常不会在 wait (),notify (),notifyAll ()的方法签名提及。

6.Sleep ()、suspend ()和 wait ()之间有什么区别?

Thread.sleep ()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt ()方法,它将唤醒那个“睡眠的”线程。

注意:sleep ()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep (),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep (),也是当前线程进入睡眠,而不是t线程。t.suspend ()是过时的方法,使用 suspend ()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend ()容易引起死锁问题。

object.wait ()使当前线程出于“不可运行”状态,和 sleep ()不同的是 wait 是 object 的方法而不是 thread。调用 object.wait ()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用 object.notify (),这样将唤醒原来等待中的线程,然后释放该锁。基本上 wait ()/notify ()与 sleep ()/interrupt ()类似,只是前者需要获取对象锁。

7.在静态方法上使用同步时会发生什么事?

同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。

8.当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?

可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java 没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据 Java 照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。

下面有一个示例说明:Common 类有两个方法 synchronizedMethod1()和 method1(),MyThread 类在独立的线程中调用这两个方法。


  
public class Common {       public synchronized void synchronizedMethod1() {   System.out.println ("synchronizedMethod1 called");   try {   Thread.sleep (1000);   } catch (InterruptedException e) {   e.printStackTrace ();   }   System.out.println ("synchronizedMethod1 done");   }   public void method1() {   System.out.println ("Method 1 called");   try {   Thread.sleep (1000);   } catch (InterruptedException e) {   e.printStackTrace ();   }   System.out.println ("Method 1 done");   }   } 

  
public class MyThread extends Thread {   private int id = 0;   private Common common;       public MyThread (String name, int no, Common object) {   super(name);   common = object;   id = no;   }       public void run () {   System.out.println ("Running Thread" + this.getName ());   try {   if (id == 0) {   common.synchronizedMethod1();   } else {   common.method1();   }   } catch (Exception e) {   e.printStackTrace ();   }   }       public static void main (String[] args) {   Common c = new Common ();   MyThread t1 = new MyThread ("MyThread-1", 0, c);   MyThread t2 = new MyThread ("MyThread-2", 1, c);   t1.start ();   t2.start ();   }   }  

这里是程序的输出:


  
Running ThreadMyThread-1   synchronizedMethod1 called   Running ThreadMyThread-2   Method 1 called   synchronizedMethod1 done   Method 1 done 

 

结果表明即使 synchronizedMethod1()方法执行了,method1()也会被调用。

9.在一个对象上两个线程可以调用两个不同的同步实例方法吗?

不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。看下面代码示例非常清晰:Common 类有 synchronizedMethod1()和 synchronizedMethod2()方法,MyThread 调用这两个方法。


  
public class Common {   public synchronized void synchronizedMethod1() {   System.out.println ("synchronizedMethod1 called");   try {   Thread.sleep (1000);   } catch (InterruptedException e) {   e.printStackTrace ();   }   System.out.println ("synchronizedMethod1 done");   }       public synchronized void synchronizedMethod2() {   System.out.println ("synchronizedMethod2 called");   try {   Thread.sleep (1000);   } catch (InterruptedException e) {   e.printStackTrace ();   }   System.out.println ("synchronizedMethod2 done");   }   } 

  
public class MyThread extends Thread {   private int id = 0;   private Common common;       public MyThread (String name, int no, Common object) {   super(name);   common = object;   id = no;   }       public void run () {   System.out.println ("Running Thread" + this.getName ());   try {   if (id == 0) {   common.synchronizedMethod1();   } else {   common.synchronizedMethod2();   }   } catch (Exception e) {   e.printStackTrace ();   }   }       public static void main (String[] args) {   Common c = new Common ();   MyThread t1 = new MyThread ("MyThread-1", 0, c);   MyThread t2 = new MyThread ("MyThread-2", 1, c);   t1.start ();   t2.start ();   }   } 

10.什么是死锁

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就 JavaAPI 而言,线程死锁可能发生在一下情况。

  • 当两个线程相互调用 Thread.join ()
  • 当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

11.什么是线程饿死,什么是活锁?

线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。

当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI 中线程活锁可能发生在以下情形:

  • 当所有线程在程序中执行 Object.wait (0),参数为 0 的 wait 方法。程序将发生活锁直到在相应的对象上有线程调用 Object.notify ()或者 Object.notifyAll ()。
  • 当所有线程卡在无限循环中。

这里的问题并不详尽,我相信还有很多重要的问题并未提及,您认为还有哪些问题应该包括在上面呢?欢迎在评论中分享任何形式的问题与建议。

转载于:https://my.oschina.net/jeeker/blog/424787

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34032621/article/details/91805331

智能推荐

js金钱千分位分隔符_js money 千分号-程序员宅基地

文章浏览阅读499次。金钱千分位分隔符:function moneyFormat(nMoney){ if(nMoney == 0){ return ‘0.00’; } if(nMoney && nMoney != null){ nMoney = String(nMoney); var sLeft = nMoney.split(’.’)[0], sRight = nMoney.split(’.’)[1]; sRight = sRight ? (sRig_js money 千分号

APM2323AAC-TRL-VB一款SOT23封装P—Channel场效应MOS管-程序员宅基地

文章浏览阅读312次,点赞8次,收藏7次。*总结:** APM2323AAC-TRL-VB是一款适用于负电压场景的P-Channel沟道MOSFET,包括负电压电源开关、负电压电池保护、负载开关和逆变器等领域。1. **负电压电源开关:** APM2323AAC-TRL-VB适用于负电压电源开关模块,特别在需要进行负电压电源管理的应用场景。3. **负载开关:** 在需要对负载进行高效控制的领域,如便携式设备或负电压负载开关模块,APM2323AAC-TRL-VB可用于负载开关,提供可靠的电流调节和保护功能。**封装:** SOT23。

2024/3/8洛谷刷题(模拟与高精度)-程序员宅基地

文章浏览阅读353次,点赞4次,收藏7次。先特判1,如果是1不输出,但是是-1的话要输出-号,所以就是(n-&&i)但是-1在常数项的时候可以出现,所以其实我们就知道了,只要是最后一项,除了0都可以输出, 而且其他系数除一外也是直接输出所以就是(abs(n)>1||i==0)1.第一考虑的肯定就是+或-号的输出啦,负数自带符号,所以只用考虑+号的输出,不用输出的有第一项和非正数(0的话也不用),0的话整项不见,第一项和负数的话我们直接输出,所以条件为(i!2.如何接下来就是考虑系数的输出了,系数特判1和-1,遇到这种一般会用到abs来解决。

python按指定概率抽样_python:抽样和抽样方法-程序员宅基地

文章浏览阅读2.4k次。学习目标目标知道总体、样本、样本大小、样本数量知道样本统计量和总体统计量知道总体分布、样本分布和抽样分布知道常用的抽样方法某糖果公司研发了一种超长效口香糖,为了得到口味持续时间的数据,公司聘请了试吃者帮忙完成检验,结果却让人大跌眼镜!没文化,真可怕!我该怎么办? 有时候数据很容易收集,例如参加健身俱乐部的人的年龄,后这一家游戏公司的销售数据。但有时候不太容易,该怎么办呢? 是时候拿出终极武器了— ..._以某种概率采样

nrf51822 nrfjprog.exe ERROR: Invalid serial number!_nrfjprog couldn't be executed-程序员宅基地

文章浏览阅读1.1k次。遇到问题: Hi,I use nRFgo Studio to erase nRF51822, like this but it display: ERROR: Invalid serial number! Is this a J-link? I’ve never seen a J-Link with a 10 digit serial number, but maybe I’m w..._nrfjprog couldn't be executed

记录一下Ant Design Vue的表单布局自适应_a-col :xs="24-程序员宅基地

文章浏览阅读3.6k次,点赞2次,收藏6次。主要是labelCol和wrapperCol的使用<a-form-model ref="infoForm" :model="formData" :rules="formRules" :label-col="labelCol" :wrapper-col="wrapperCol"> <a-row_a-col :xs="24

随便推点

ubuntu 16.04下安装docker后,执行docker出现权限不足的解决办法_linux docker: you are not authorized to perform th-程序员宅基地

文章浏览阅读1.7k次。ubuntu 16.04下安装docker,具体可见下面的连接:https://blog.csdn.net/jinking01/article/details/82490688使用sudo安装docker完成后,普通用户执行docker ps,报错connect: permission denied,链接权限被拒绝。具体解决办法为:1.添加当前用户到docker 用户组sudo gpasswd -a ${USER} docker2.查看用户组下用户,检查添加是否成功cat /etc/grou_linux docker: you are not authorized to perform this operation: server retur

离群值是什么意思_学术必备!代谢组学及数据分析相关问题汇总-程序员宅基地

文章浏览阅读2.2k次。为方便大家快速地掌握代谢组学及数据分析相关知识,现把咨询我们的有关代谢组学及数据分析的一些问题给大家整理出来,供大家参考。1.PCA:loading图,P=COSα中P代表什么意思?The loading, p, for a selected PCA dimension, represent the importance of the X variables in that dimension。2..._代谢组学pca有一组样品离散

STM32·HAL库开发(十八)不同芯片间程序的移植——案例:STM32F103C8T6程序移植到STM32F103RCT6_基于hal库的stm32ct86和stm32rct6能移植嘛-程序员宅基地

文章浏览阅读733次。不同芯片间程序的移植——案例:STM32F103C8T6程序移植到STM32F103RCT6_基于hal库的stm32ct86和stm32rct6能移植嘛

JavaScript数组和字符串的方法总结_javascript 字符串数组-程序员宅基地

文章浏览阅读1k次,点赞13次,收藏13次。参数为回调函数,会遍历数组所有的项,回调函数接受三个参数,分别为value,index,self;反向归井,同forEach,迭代数组的所有项,并构建一个最终值,由reduceRight返回。同forEach,同时回调函数返回布尔值,为true的数据组成新数组由filter返回。同forEach,同时回调函数返回布尔值,只要由一个为true,由some返回true。归并,同forEach,迭代数组的所有项,并构建一个最终值,由reduce返回。提取字符串的片断,并在新的字符串中返回被提取的部分。_javascript 字符串数组

新浪实时股票数据接口http://hq.sinajs.cn/list=code_hq.sinajs.cn、-程序员宅基地

文章浏览阅读6.9w次,点赞17次,收藏59次。股票数据的获取目前有如下两种方法可以获取:1.http/javascript接口取数据2. web-service接口1.http/javascript接口取数据1.1Sina股票数据接口以大秦铁路(股票代码:601006)为例,如果要获取它的最新行情,只需访问新浪的股票数据接口:http://hq.sinajs.cn/list=sh601006这个url会返回一串_hq.sinajs.cn、

性能测试jmeter连接数据库jdbc(sql server举例)_jmeter压测 sql server-程序员宅基地

文章浏览阅读121次。2. 进入maven仓库-http://mvnrepository.com/,找到想要的工具包,比如我想要连接的是sql server数据库,就在搜索栏中搜索sql server,如果是mysql就搜mysql。B. 长期使用的话:放到JMeter的安装路径的lib目录下,就不需要引入了,使用时需要重启一次JMeter。3. 点击第一个搜索结果,下载最高的版本(高版本可以向下兼容,推荐高版本),点击Files下载jar包。2. 查看结果树:点击JDBC Request可以显示当前请求查到的所有值。_jmeter压测 sql server