`

java多线程

 
阅读更多

摘要:

很多核心Java面试题来源于多线程(Multi-Threading)和集合框架(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的。这篇文章收集了 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 类在独立的线程中调用这两个方法。

  1. public class Common {     
  2.     public synchronized void synchronizedMethod1() {    
  3.         System.out.println ("synchronizedMethod1 called");    
  4.         try {    
  5.             Thread.sleep (1000);    
  6.         } catch (InterruptedException e) {    
  7.             e.printStackTrace ();    
  8.         }    
  9.         System.out.println ("synchronizedMethod1 done");    
  10.     }    
  11.     public void method1() {    
  12.         System.out.println ("Method 1 called");    
  13.         try {    
  14.             Thread.sleep (1000);    
  15.         } catch (InterruptedException e) {    
  16.             e.printStackTrace ();    
  17.         }    
  18.         System.out.println ("Method 1 done");    
  19.     }    
  20. }   
  21. public class MyThread extends Thread {    
  22.     private int id = 0;    
  23.     private Common common;    
  24.      
  25.     public MyThread (String name, int no, Common object) {    
  26.         super(name);    
  27.         common = object;    
  28.         id = no;    
  29.     }    
  30.      
  31.     public void run () {    
  32.         System.out.println ("Running Thread" + this.getName ());    
  33.         try {    
  34.             if (id == 0) {    
  35.                 common.synchronizedMethod1();    
  36.             } else {    
  37.                 common.method1();    
  38.             }    
  39.         } catch (Exception e) {    
  40.             e.printStackTrace ();    
  41.         }    
  42.     }    
  43.      
  44.     public static void main (String[] args) {    
  45.         Common c = new Common ();    
  46.         MyThread t1 = new MyThread ("MyThread-1"0, c);    
  47.         MyThread t2 = new MyThread ("MyThread-2"1, c);    
  48.         t1.start ();    
  49.         t2.start ();    
  50.     }    
  51. }  
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 调用这两个方法。

  1. public class Common {    
  2.     public synchronized void synchronizedMethod1() {    
  3.         System.out.println ("synchronizedMethod1 called");    
  4.         try {    
  5.             Thread.sleep (1000);    
  6.         } catch (InterruptedException e) {    
  7.             e.printStackTrace ();    
  8.         }    
  9.         System.out.println ("synchronizedMethod1 done");    
  10.     }    
  11.      
  12.     public synchronized void synchronizedMethod2() {    
  13.         System.out.println ("synchronizedMethod2 called");    
  14.         try {    
  15.             Thread.sleep (1000);    
  16.         } catch (InterruptedException e) {    
  17.             e.printStackTrace ();    
  18.         }    
  19.         System.out.println ("synchronizedMethod2 done");    
  20.     }     
  21. }   
  22.   
  23.   
  24. public class MyThread extends Thread {    
  25.     private int id = 0;    
  26.     private Common common;    
  27.          
  28.     public MyThread (String name, int no, Common object) {    
  29.         super(name);    
  30.         common = object;    
  31.         id = no;    
  32.     }    
  33.      
  34.     public void run () {    
  35.         System.out.println ("Running Thread" + this.getName ());    
  36.         try {    
  37.             if (id == 0) {    
  38.                 common.synchronizedMethod1();    
  39.             } else {    
  40.                 common.synchronizedMethod2();    
  41.             }    
  42.         } catch (Exception e) {    
  43.             e.printStackTrace ();    
  44.         }    
  45.     }    
  46.      
  47.     public static void main (String[] args) {    
  48.         Common c = new Common ();    
  49.         MyThread t1 = new MyThread ("MyThread-1"0, c);    
  50.         MyThread t2 = new MyThread ("MyThread-2"1, c);    
  51.         t1.start ();    
  52.         t2.start ();    
  53.     }    
  54. }   
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 ()。
当所有线程卡在无限循环中。

这里的问题并不详尽,我相信还有很多重要的问题并未提及,您认为还有哪些问题应该包括在上面呢?欢迎在评论中分享任何形式的问题与建议。
本文转自:http://www.csdn.net/article/2012-05-28/2806046

分享到:
评论

相关推荐

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java多线程读取文件

    Java多线程读大文件 java多线程写文件:多线程往队列中写入数据

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    java 多线程操作数据库

    一个java 多线程操作数据库应用程序!!!

    java多线程经典案例

    java多线程经典案例,线程同步、线程通信、线程阻塞等经典案例

    Java多线程编程技术

    《Java多线程编程核心技术》建议猿友们读两遍,因为其写得没有那么抽象,第一遍有些概念不是很理解,可以先跳过并记录起来,第一遍阅读的目的主要是了解整个架构。第二遍再慢慢品味,并贯穿全部是指点来思考,并将...

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...

    Java多线程知识点总结

    该文档总结了Java多线程相关的知识点,分享给大家,简单易懂!

    java多线程的讲解和实战

    详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。

    java多线程通信图解

    一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,...

    java多线程处理数据库数据

    java多线程处理数据库数据,使用并发包,无框架,可批量处数据库数据,进行增删改。。等等操作。

    java多线程,对多线程,线程池进行封装,方便使用

    java多线程,对多线程,线程池进行封装,方便使用

    Java多线程编程经验

    现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 线程是指进程中的一个执行流程,一个进程中可以运行多个线程。...本文档提供Java多线程编程经验,方便广大Java爱好者研究学习Java多线程

    java多线程处理大数据

    java多线程处理大数据,可根据配置的线程数,任务去调度处理

    java多线程并发

    java多线程并发的在新窗口

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...

    java多线程核心技术

    资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如...

    java多线程实现大批量数据导入源码

    java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制

    java多线程查询数据库

    java多线程并发查询数据库,使用线程池控制分页,并发查询。

    java多线程模拟队列实现排队叫号

    java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号 java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号

Global site tag (gtag.js) - Google Analytics