没有合适的资源?快使用搜索试试~ 我知道了~
java多线程设计模式.docx
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 102 浏览量
2023-02-28
20:47:21
上传
评论
收藏 162KB DOCX 举报
温馨提示
试读
24页
。
资源推荐
资源详情
资源评论
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()方法。例如:
public class MyThread extends Thread {
public run() {
System.out.println("MyThread.run()");
}
}
在合适的地方启动线程:new MyThread().start();
方法 2:如果自己的类已经extends 另一个类,就无法直接extends Thread,此时,必须实现
一个 Runnable 接口:
public class MyThread extends OtherClass implements Runnable {
public run() {
System.out.println("MyThread.run()");
}
}
为了启动 MyThread,需要首先实例化一个 Thread,并传入自己的 MyThread 实例:
MyThread myt = new MyThread();
Thread t = new Thread(myt);
t.start();
事实上,当传入一个 Runnable target 参数给 Thread 后,Thread 的 run()方法就会调用
target.run(),参考 JDK 源代码:
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()可以同时由两个线程来执行。
多线程同步的实现最终依赖锁机制。我们可以想象某一共享资源是一间屋子,每个人都是一
个线程。当 A 希望进入房间时,他必须获得门锁,一旦 A 获得门锁,他进去后就立刻将门
锁上,于是 B,C,D...就不得不在门外等待,直到 A 释放锁出来后,B,C,D...中的某一人抢到
了该锁(具体抢法依赖于 JVM 的实现,可以先到先得,也可以随机挑选),然后进屋又将门
锁上。这样,任一时刻最多有一人在屋内(使用共享资源)。
Java 语言规范内置了对多线程的支持。对于Java 程序来说,每一个对象实例都有一把“锁”,
一旦某个线程获得了该锁,别的线程如果希望获得该锁,只能等待这个线程释放锁之后。获
得锁的方法只有一个,就是 synchronized 关键字。例如:
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; }事实上相当于:
public void setCount(int count) {
synchronized(this) { // 在此获得 this 锁
this.count = count;
} // 在此释放 this 锁
}
红色部分表示需要同步的代码段,该区域为“危险区域”,如果两个以上的线程同时执行,会
引发冲突,因此,要更改 SharedResource 的内部状态,必须先获得 SharedResource 实例的锁。
退出 synchronized 块时,线程拥有的锁自动释放,于是,别的线程又可以获取该锁了。
为了提高性能,不一定要锁定 this,例如,SharedResource 有两个独立变化的变量:
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()。为了提高性能,可以使
用不同对象的锁:
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;
}
}
}
通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程 displayThread 想要
执行显示图片的任务,必须等待下载线程 downloadThread 将该图片下载完毕。如果图片还
没 有 下 载 完 , displayThread 可 以 暂 停 , 当 downloadThread 完 成 了 任 务 后 , 再 通 知
displayThread“图片准备完毕,可以显示了”,这时,displayThread 继续执行。
以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被
唤醒。在Java 中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例
如:
synchronized(obj) {
while(!condition) {
obj.wait();
}
obj.doSomething();
}
当线程 A 获得了 obj 锁后,发现条件 condition 不满足,无法继续下一处理,于是线程 A 就
wait()。
在另一线程 B 中,如果B 更改了某些条件,使得线程A 的 condition 条件满足了,就可以唤
醒线程 A:
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/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。
GuardedSuspention 模式主要思想是:
当条件不满足时,线程等待,直到条件满足时,等待该条件的线程被唤醒。
我们设计一个客户端线程和一个服务器线程,客户端线程不断发送请求给服务器线程,服务
器线程不断处理请求。当请求队列为空时,服务器线程就必须等待,直到客户端发送了请求。
先定义一个请求队列:Queue
package com.crackj2ee.thread;
import java.util.*;
public class Queue {
private List queue = new LinkedList();
剩余23页未读,继续阅读
资源评论
不吃鸳鸯锅
- 粉丝: 8246
- 资源: 2万+
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功