http://xuefeng.javaeedev.com
线程的创建和启动
Java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就结束了。一旦一个线程执行完毕,这个实例就不能再重新启动,只能重新生成一个新实例,再启动一个新线程。
Thread类是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法:
Thread t = new Thread();
t.start();
start()方法是一个native方法,它将启动一个新线程,并执行run()方法。Thread类默认的run()方法什么也不做就退出了。注意:直接调用run()方法并不会启动一个新线程,它和调用一个普通的java方法没有什么区别。
因此,有两个方法可以实现自己的线程:
方法1:自己的类extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:
java 代码
- public class MyThread extends Thread {
- public run() {
- System.out.println("MyThread.run()");
- }
- }
在合适的地方启动线程:new MyThread().start();
方法2:如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口:
java 代码
- public class MyThread extends OtherClass implements Runnable {
- public run() {
- System.out.println("MyThread.run()");
- }
- }
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:
java 代码
- MyThread myt = new MyThread();
- Thread t = new Thread(myt);
- t.start();
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
java 代码
- public void run() {
- if (target != null) {
- target.run();
- }
- }
线程还有一些Name, ThreadGroup, isDaemon等设置,由于和线程设计模式关联很少,这里就不多说了。
线程的同步
由于同一进程内的多个线程共享内存空间,在Java中,就是共享实例,当多个线程试图同时修改某个实例的内容时,就会造成冲突,因此,线程必须实现共享互斥,使多线程同步。
最简单的同步是将一个方法标记为synchronized,对同一个实例来说,任一时刻只能有一个synchronized方法在执行。当一个方法 正在执行某个synchronized方法时,其他线程如果想要执行这个实例的任意一个synchronized方法,都必须等待当前执行 synchronized方法的线程退出此方法后,才能依次执行。
但是,非synchronized方法不受影响,不管当前有没有执行synchronized方法,非synchronized方法都可以被多个线程同时执行。
此外,必须注意,只有同一实例的synchronized方法同一时间只能被一个线程执行,不同实例的synchronized方法是可以并发的。 例如,class A定义了synchronized方法sync(),则不同实例a1.sync()和a2.sync()可以同时由两个线程来执行。
Java锁机制
多线程同步的实现最终依赖锁机制。我们可以想象某一共享资源是一间屋子,每个人都是一个线程。当A希望进入房间时,他必须获得门锁,一旦 A获得门锁,他进去后就立刻将门锁上,于是B,C,D...就不得不在门外等待,直到A释放锁出来后,B,C,D...中的某一人抢到了该锁(具体抢法依 赖于 JVM的实现,可以先到先得,也可以随机挑选),然后进屋又将门锁上。这样,任一时刻最多有一人在屋内(使用共享资源)。
Java语言规范内置了对多线程的支持。对于Java程序来说,每一个对象实例都有一把“锁”,一旦某个线程获得了该锁,别的线程如果希望获得该锁,只能等待这个线程释放锁之后。获得锁的方法只有一个,就是synchronized关键字。例如:
java 代码
- public class SharedResource {
- private int count = 0;
-
- public int getCount() { return count; }
-
- public synchronized void setCount(int count) { this.count = count; }
-
- }
同步方法public synchronized void setCount(int count) { this.count = count; } 事实上相当于:
java 代码
- public void setCount(int count) {
- synchronized(this) {
- this.count = count;
- }
- }
红色部分表示需要同步的代码段,该区域为“危险区域”,如果两个以上的线程同时执行,会引发冲突,因此,要更改SharedResource的内部状态,必须先获得SharedResource实例的锁。
退出synchronized块时,线程拥有的锁自动释放,于是,别的线程又可以获取该锁了。
为了提高性能,不一定要锁定this,例如,SharedResource有两个独立变化的变量:
java 代码
- public class SharedResouce {
- private int a = 0;
- private int b = 0;
-
- public synchronized void setA(int a) { this.a = a; }
-
- public synchronized void setB(int b) { this.b = b; }
- }
若同步整个方法,则setA()的时候无法setB(),setB()时无法setA()。为了提高性能,可以使用不同对象的锁:
java 代码
- public class SharedResouce {
- private int a = 0;
- private int b = 0;
- private Object sync_a = new Object();
- private Object sync_b = new Object();
-
- public void setA(int a) {
- synchronized(sync_a) {
- this.a = a;
- }
- }
-
- public synchronized void setB(int b) {
- synchronized(sync_b) {
- this.b = b;
- }
- }
- }
wait/notify机制
通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程 downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务 后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。
以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如:
java 代码
- synchronized(obj) {
- while(!condition) {
- obj.wait();
- }
- obj.doSomething();
- }
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:
java 代码
- synchronized(obj) {
- condition = true;
- obj.notify();
- }
需要注意的概念是:
# 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {...} 代码段内。
# 调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {...} 代码段内唤醒A。
# 当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
# 如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
# obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
# 当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
wait/sleep的区别
前面讲了wait/notify机制,Thread还有一个sleep()静态方法,它也能使线程暂停一段时间。sleep与wait的 不同点是: sleep并不释放锁,并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。
但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在 wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException。
分享到:
相关推荐
为能和大家能共同探讨"设计模式",我将自己在学习中的心得写下来,只是想帮助更多人更容易理解 GoF 的《设计模式》。由 于原著都是以C++为例, 以Java为例的设计模式基本又都以图形应用为例,而我们更关心Java在中间件等...
- 《深入理解Java虚拟机:JVM高级特性与最佳实践》:周志明的书,深入讲解JVM内存模型和多线程,对并发编程有较大帮助。 - 《Java EE 6 权威指南.基础篇.Basic concepts》:尽管现代开发更多依赖Spring等框架,...
10. **设计模式**:习题可能涉及到一些常见的设计模式,如单例、工厂、观察者、装饰器等,设计模式是解决常见编程问题的通用解决方案。 通过解答这些习题,学习者不仅能巩固Java语言的基础,还能提高解决实际问题的...
Java作为一门广泛使用的编程语言,其面试题涵盖了众多的知识领域,包括基础语法、面向对象、集合框架、多线程、异常处理、IO流、网络编程、设计模式、JVM优化、数据库操作等。以下是一些Java面试中常被问到的知识点...
8. **设计模式**:良好的软件设计通常会运用设计模式,比如单例模式用于控制抽奖逻辑的实例化,工厂模式用于创建GUI组件,观察者模式用于更新界面状态等。 9. **程序测试**:代码已经测试并确认无误,这意味着...
过滤流(FilterStream)是Java I/O中的一种设计模式,它们在已存在的流之上添加额外功能。例如,DataInputStream和DataOutputStream扩展了字节流,增加了对基本类型数据的读写支持;而BufferedInputStream和...
9. 设计模式:总结Java中常用的23种设计模式的定义、使用场景和实现方式。 10. Spring/SpringMVC:介绍Spring框架的核心特性,包括IoC容器、AOP、事务管理、Spring MVC框架等。 11. SpringBoot/SpringCloud:...
9. **设计模式**:可能包含一些常见设计模式的实现,如单例、工厂、观察者等。 10. **异常与日志记录**:如何有效地捕获和记录程序中的异常信息。 11. **反射机制**:用于在运行时动态获取类的信息和操控对象。 ...
3. **多线程** - **线程状态**:新建、运行、阻塞、等待、终止等五种状态。 - **同步机制**:synchronized关键字,wait()、notify()和notifyAll()方法,以及Lock接口。 - **并发工具类**:如CountDownLatch、...
《深入理解Java虚拟机》、《并发编程的艺术》、《Java多线程核心编程艺术》、《Java8函数式编程》、《Redis设计与实现》、《RocketMQ技术内幕》、《Spring技术内幕》、《Spring源码深度解析》、《剑指Offer》、...
【Java】在Java部分,面试可能涵盖基础语法、面向对象编程、集合框架、多线程、异常处理、IO流、JVM内存模型以及设计模式等方面。例如,可能会问到如何优化代码性能,如何处理并发问题,或者对Java 8的新特性如...
3.Java语言的特点简单、解释性、面向对象、健壮、动态、高性能、多线程、分布式处理、安全性、开源、结构中立、跨平台。 3大特性 (1).安全性 (2).虚拟机JVM(一次编译到处运行) (3).GC垃圾回收机制 开发环境...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11.1 多线程的servlet模型 350 11.2 线程安全的servlet 351 11.2.1 变量的线程安全 351 11.2.2 属性的线程安全 360 11.3 singlethreadmodel接口 362 11.4 小结 363 11.5 思考题 363 第3部分 jsp篇 第12章 ...
11. **代码结构与设计模式**:良好的代码组织和设计模式(如工厂模式、观察者模式)可以使项目更易于维护和扩展。 通过学习和实践这个【小小图片爬虫】项目,开发者不仅可以掌握HTTP请求的基本操作,还能了解到如何...
11. 设计模式:单例、工厂、装饰器等23种设计模式的实例解析。 12. 数据库操作:SQL语言、JDBC编程、ORM框架(如Hibernate)等。 13. 测试:单元测试、集成测试,JUnit、Mockito等工具的使用。 博主可能通过Word...
过滤器链的设计模式使得功能模块化,方便代码复用和扩展。 2. **Session**:MINA中的Session代表了客户端与服务器之间的一个会话。它包含了会话状态,如连接状态、读写缓冲区、心跳机制等,同时也提供了发送和接收...