很多核心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 ()。
- 当所有线程卡在无限循环中。
这里的问题并不详尽,我相信还有很多重要的问题并未提及,您认为还有哪些问题应该包括在上面呢?欢迎在评论中分享任何形式的问题与建议。
译文出自:jobbole
英文出自:fromdev
Java中使用的路径,分为两种:绝对路径和相对路径。归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的!
在开发Web方面的应用时, 经常需要获取 服务器中当前WebRoot的物理路径。
如果是Servlet , Action , Controller, 或则Filter , Listener , 拦截器等相关类时, 我们只需要获得ServletContext, 然后通过ServletContext.getRealPath("/")来获取当前应用在服务器上的物理地址。
如果在类中取不到ServletContext时, 有两种方式可以做到:
1. 利用Java的类加载机制 调用 XXX.class.getClassLoader().getResource(""); 方法来获取到ClassPath , 然后处理获得WebRoot目录,这种方式只能是该class在WebRoot/WEB-INF/classes下才能生效, 如果该class被打包到一个jar文件中, 则该方法失效。这时就应该用下面一种方式。
2. spring框架的思路, 在WEB-INF/web.xml中 , 创建一个webAppRootKey的param, 指定一个值(默认为webapp.root)作为键值, 然后通过Listener , 或者Filter , 或者Servlet 执行String webAppRootKey = getServletContext().getRealPath("/"); 并将webAppRootKey对应的webapp.root 分别作为Key , Value写到System Properties系统属性中。之后在程序中通过System.getProperty("webapp.root")来获得WebRoot的物理路 径。
根据第二种的思路,我们还可以再扩展一下。不过对于在部署在一台服务器中的应用来说,若还不是你所需请再往下看。
下面是一些得到classpath和当前类的绝对路径的一些方法。你可使用其中的一些方法来得到你需要的资源的绝对路径:
1. DebitNoteAction.class.getResource("")
得到的是当前类FileTest.class文件的URI目录。不包括自己!
如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/
atacarnet/src/com/evi/modules/atacarnet/action/
2. DebitNoteAction.class.getResource("/")
得到的是当前的classpath的绝对URI路径。
如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/
3. Thread.currentThread().getContextClassLoader().getResource("")
得到的也是当前ClassPath的绝对URI路径
如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/
4. DebitNoteAction.class.getClassLoader().getResource("") 或ClassLoader.getSystemResource("")
得到的也是当前ClassPath的绝对URI路径。
如:file:/D:/eclipse/springTest/WebRoot/WEB-INF/classes/
5. 取得服务器相对路径
System.getProperty("user.dir")
例如:E:\apache-tomcat-5.5.16\apache-tomcat-5.5.16\bin
我推荐使用Thread.currentThread().getContextClassLoader().getResource("")来得到当前的classpath的绝对路径的URI表示法
6. 取得项目中的绝对路径
一般用request.getRealPath("/")或request.getRealPath("/config/")
但现在不提倡使用request.getRealPath("/")了,大家可试用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径
要取得src的文件非常容易,因为src是默认的相对目录,比如你说要取得src下com目录的test.java文件,你只需要这样就够了
File f = new File(com/test.java);
但如果我要取得不在src目录或者WebRoot目录下的文件呢,而是要从src或者WebRoot同级的目录中取呢,比如说doc吧
我的硬方法是这样实现的:
String path = this.getServletContext().getRealPath("/");
Properties p = new Properties();
p.load(new FileInputStream(new File(path.substring(0,(path.lastIndexOf("\\WebRoot") + 1)) + "doc/db.properties")));
System.out.println(p.getProperty("driverName"));
原文链接:http://www.cn-java.com/www1/?action-viewnews-itemid-78354
酷壳版主陈皓近日发表博文《JDK里的设计模式》,文中他列出了JDK中有关23个经典设计模式的示例。现把全文转载于此,全文如下:
下面是JDK中有关23个经典设计模式的示例:
Structural(结构模式)
Adapter:
把一个接口或是类变成另外一种。
- ● java.util.Arrays#asList()
- ● javax.swing.JTable(TableModel)
- ● java.io.InputStreamReader(InputStream)
- ● java.io.OutputStreamWriter(OutputStream)
- ● javax.xml.bind.annotation.adapters.XmlAdapter#marshal()
- ● javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal()
Bridge:
把抽象和实现解藕,于是接口和实现可在完全独立开来。
- ● AWT (提供了抽象层映射于实际的操作系统)
- ● JDBC
Composite:
让使用者把单独的对象和组合对象混用。
- ● javax.swing.JComponent#add(Component)
- ● java.awt.Container#add(Component)
- ● java.util.Map#putAll(Map)
- ● java.util.List#addAll(Collection)
- ● java.util.Set#addAll(Collection)
Decorator:
为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类。这个模式在JDK中几乎无处不在,所以,下面的列表只是一些典型的。
- ● java.io.BufferedInputStream(InputStream)
- ● java.io.DataInputStream(InputStream)
- ● java.io.BufferedOutputStream(OutputStream)
- ● java.util.zip.ZipOutputStream(OutputStream)
- ● java.util.Collections#checked[List|Map|Set|SortedSet|SortedMap]()
Facade:
用一个简单的接口包状一组组件,接口,抽象或是子系统。
- ● java.lang.Class
- ● javax.faces.webapp.FacesServlet
Flyweight:
有效率地存储大量的小的对象。
- ● java.lang.Integer#valueOf(int)
- ● java.lang.Boolean#valueOf(boolean)
- ● java.lang.Byte#valueOf(byte)
- ● java.lang.Character#valueOf(char)
Proxy:
用一个简单的对象来代替一个复杂的对象。
- ● java.lang.reflect.Proxy
- ● RMI
Creational(创建模式)
Abstract factory:
创建一组有关联的对象实例。这个模式在JDK中也是相当的常见,还有很多的framework例如Spring。我们很容易找到这样的实例。
- ● java.util.Calendar#getInstance()
- ● java.util.Arrays#asList()
- ● java.util.ResourceBundle#getBundle()
- ● java.sql.DriverManager#getConnection()
- ● java.sql.Connection#createStatement()
- ● java.sql.Statement#executeQuery()
- ● java.text.NumberFormat#getInstance()
- ● javax.xml.transform.TransformerFactory#newInstance()
Builder:
主要用来简化一个复杂的对象的创建。这个模式也可以用来实现一个 Fluent Interface。
- ● java.lang.StringBuilder#append()
- ● java.lang.StringBuffer#append()
- ● java.sql.PreparedStatement
- ● javax.swing.GroupLayout.Group#addComponent()
Factory:
简单来说,按照需求返回一个类型的实例。
- ● java.lang.Proxy#newProxyInstance()
- ● java.lang.Object#toString()
- ● java.lang.Class#newInstance()
- ● java.lang.reflect.Array#newInstance()
- ● java.lang.reflect.Constructor#newInstance()
- ● java.lang.Boolean#valueOf(String)
- ● java.lang.Class#forName()
Prototype:
使用自己的实例创建另一个实例。有时候,创建一个实例然后再把已有实例的值拷贝过去,是一个很复杂的动作。所以,使用这个模式可以避免这样的复杂性。
- ● java.lang.Object#clone()
- ● java.lang.Cloneable
Singleton:
只允许一个实例。在 Effective Java中建议使用Emun.
- ● java.lang.Runtime#getRuntime()
- ● java.awt.Toolkit#getDefaultToolkit()
- ● java.awt.GraphicsEnvironment#getLocalGraphicsEnvironment()
- ● java.awt.Desktop#getDesktop()
Behavioral(行为模式)
Chain of responsibility:
把一个对象在一个链接传递直到被处理。在这个链上的所有的对象有相同的接口(抽象类)但却有不同的实现。
- ● java.util.logging.Logger#log()
- ● javax.servlet.Filter#doFilter()
Command:
把一个或一些命令封装到一个对象中。
- ● java.lang.Runnable
- ● javax.swing.Action
Interpreter:
一个语法解释器的模式。
- ● java.util.Pattern
- ● java.text.Normalizer
- ● java.text.Format
Iterator:
提供一种一致的方法来顺序遍历一个容器中的所有元素。
- ● java.util.Iterator
- ● java.util.Enumeration
Mediator:
用来减少对象单的直接通讯的依赖关系。使用一个中间类来管理消息的方向。
- ● java.util.Timer
- ● java.util.concurrent.Executor#execute()
- ● java.util.concurrent.ExecutorService#submit()
- ● java.lang.reflect.Method#invoke()
Memento:
给一个对象的状态做一个快照。Date类在内部使用了一个long型来做这个快照。
- ● java.util.Date
- ● java.io.Serializable
Null Object:
这个模式用来解决如果一个Collection中没有元素的情况。
- ● java.util.Collections#emptyList()
- ● java.util.Collections#emptyMap()
- ● java.util.Collections#emptySet()
Observer:
允许一个对象向所有的侦听的对象广播自己的消息或事件。
- ● java.util.EventListener
- ● javax.servlet.http.HttpSessionBindingListener
- ● javax.servlet.http.HttpSessionAttributeListener
- ● javax.faces.event.PhaseListener
State:
这个模式允许你可以在运行时很容易地根据自身内部的状态改变对象的行为。
- ● java.util.Iterator
- ● javax.faces.lifecycle.LifeCycle#execute()
Strategy:
定义一组算法,并把其封装到一个对象中。然后在运行时,可以灵活的使用其中的一个算法。
- ● java.util.Comparator#compare()
- ● javax.servlet.http.HttpServlet
- ● javax.servlet.Filter#doFilter()
Template method:
允许子类重载部分父类而不需要完全重写。
- ● java.util.Collections#sort()
- ● java.io.InputStream#skip()
- ● java.io.InputStream#read()
- ● java.util.AbstractList#indexOf()
Visitor:
作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.
- ● javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor
- ● javax.lang.model.type.TypeMirror 和javax.lang.model.type.TypeVisitor
在stakeoverflow也有相应的讨论:
http://stackoverflow.com/questions/1673841/examples-of-gof-design-patterns
原文链接:http://coolshell.cn/articles/3320.html
相关推荐
7. **设计模式**:熟悉并能够灵活应用常见的23种设计模式,如单例、工厂、观察者、装饰器、适配器等,这是衡量一个程序员设计能力和解决问题能力的重要标准。 8. **数据库**:虽然Java面试不直接涉及数据库编程,但...
《Java程序员面试宝典2010版》是针对Java开发者进行面试准备的重要参考资料,它汇集了众多IT公司的面试和笔试题目,旨在帮助求职者掌握关键的Java技术知识,提高面试成功率。以下是对该宝典中可能涵盖的主要知识点的...
《程序员面试宝典》一书聚焦于Java程序员的高级面试准备,提炼...这些问题涵盖了Java语言的基础语法、核心API、设计模式、并发编程等多个方面,深入理解并掌握这些知识,将极大提升Java程序员的技术水平和面试成功率。
"java程序员面试葵花宝典"和"spring面试题大全"这两个文档标题暗示了它们将涵盖Java编程语言及其Spring框架的相关面试知识点。以下是根据这些标题和描述可能涉及的一些关键知识点的详细说明: 1. **Java基础** - *...
《Java程序员面试笔试宝典》是一本专门为Java程序员准备的面试和笔试参考资料,由何昊编著并进行了精心校对,以确保内容的准确性和实用性。这本书主要涵盖了Java基础、面试技巧、笔试常见问题以及程序员入门必备的...
Java程序员面试题是每个准备Java面试的开发者都需关注的重点。在面试过程中,这些问题往往能够反映出应聘者的编程基础、问题解决能力以及对Java特性的理解程度。以下是一些可能出现在Java程序员面试中的关键知识点,...
《黑马程序员面试宝典(java)2018版》是一本专门为Java开发者准备的面试指南,涵盖了大量在面试过程中可能遇到的问题和知识点。这本宝典由黑马程序员机构精心编纂,汇集了近万名学员的实际面试经验,为求职者提供了...
Java程序员在面试中可能会遇到各种各样的问题,涵盖语言特性、设计模式、框架以及最佳实践等多个方面。以下是一些常见的Java面试知识点: 1. **面向对象的特征**:包括抽象、继承、封装和多态性。 - **抽象**:...
6. **设计模式**:常见的23种设计模式,如工厂模式、单例模式、观察者模式、装饰者模式、适配器模式等,以及如何在实际项目中应用它们。 7. **网络编程**:TCP/IP协议、HTTP协议的基本概念,以及Java中的Socket编程...
- **技能展示**:突出JAVA相关技能,包括但不限于JDK基础知识、常用框架(如Spring、Hibernate)、数据库操作、多线程编程等,以及对设计模式的理解和应用。 ##### 面试准备 - **技术面试**:熟悉常见算法和数据...
本书分为多个章节,涵盖Java基础、面向对象编程、集合框架、多线程编程、异常处理、设计模式等关键领域。 #### 二、Java基础知识 **1. Java语言特点** - **跨平台性**:通过Java虚拟机(JVM),Java程序可以在...
- 单例模式、工厂模式、观察者模式等经典设计模式的讲解。 - 设计模式在实际项目中的应用实例分析。 5. **Spring框架**: - Spring框架的核心功能:DI(依赖注入)、AOP(面向切面编程)。 - Spring MVC的工作...
在Java开发者的职业生涯中,面试是至关重要的一个环节,它决定了你能否进入理想的公司,进一步提升自己的技能。这份2018年的Java经典就业面试大纲全面覆盖了Java基础到进阶的各个方面,旨在帮助求职者做好充分准备,...
【JAVA程序员面试32问详解】 ...这些面试问题覆盖了Java的基础语法、面向对象、集合框架、并发编程等多个方面,是Java程序员面试中常见的考点。理解和掌握这些问题,将有助于提高面试表现和实际编程能力。
如果设计一个新的设计模式,可以命名为“智能代理模式”,用于智能地代理复杂操作。 #### 代码重构 - **步骤**:识别问题区域、编写测试、重构代码、验证测试。 - **原因**:提高代码质量、增强可维护性。 #### ...
以上只是Java面试中常见的部分知识点,Java程序员需要掌握的远不止这些,包括但不限于集合框架、多线程、网络编程、IO流、设计模式、数据库连接、JVM内存管理等。在实际面试中,还需要深入理解这些概念并能应用到...
Java基础面试题目主要涵盖了许多核心概念,包括语言特性、...以上是Java基础面试题目的核心知识点详解,涵盖了Java语言、集合框架、多线程、异常处理、面向对象、Web开发等多个方面,是Java程序员面试必备的知识体系。
对于程序员来说,掌握扎实的Java基础知识是至关重要的,这不仅有助于日常开发,也是面试时必不可少的知识储备。以下是一些Java基础面试题中可能涉及的重要知识点: 1. **Java语法基础**: - 变量、数据类型(基本...