2. Guarded suspension mode and Balking mode¶
Guarded suspension mode 1 以及 Balking mode 2 都是是类似于 “附加条件的 synchronized” 这样的模式。
这两者在条件达成的情况下的执行情况与一般的 single threaded execution mode一样; 但是当条件不能满足其继续运行下去时,前者则暂停(执行wait)并等待条件达成(执行notifyAll), 后者则直接中断方法的运行。
2.1. Guarded suspension mode 实例(等待我准备好)¶
类名 |
说明 |
|---|---|
Request |
表示一个请求类 |
RequestQueue |
存放请求的类 |
ClientThread |
发送请求的类 |
ServerThread |
接收请求的类 |
Main |
测试类 |
2.1.1. Request¶
该类是 immutable 类,是线程安全的。
public class Request {
private final String name;
public Request(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return "[ Request " + name + " ]";
}
}
2.1.2. RequestQueue¶
LinkedList对象是非线程安全的。对于wait的唤醒条件notify/notifyAll放置的位置需要考量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | import java.util.Queue; import java.util.LinkedList; public class RequestQueue { private final Queue<Request> queue = new LinkedList<Request>(); public synchronized Request getRequest() { // 确认队列是否为空 while (queue.peek() == null) { try { wait(); } catch (InterruptedException e) { } } // 将队列中的第一个/头部对象取出 return queue.remove(); } public synchronized void putRequest(Request request) { // 向队列尾部中添加request queue.offer(request); notifyAll(); } } |
2.1.3. ClientThread¶
import java.util.Random;
public class ClientThread extends Thread {
private final Random random;
private final RequestQueue requestQueue;
public ClientThread(RequestQueue requestQueue, String name, long seed) {
super(name);
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = new Request("No." + i);
System.out.println(Thread.currentThread().getName() + " requests " + request);
requestQueue.putRequest(request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
2.1.4. ServerThread¶
import java.util.Random;
public class ServerThread extends Thread {
private final Random random;
private final RequestQueue requestQueue;
public ServerThread(RequestQueue requestQueue, String name, long seed) {
super(name);
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = requestQueue.getRequest();
System.out.println(Thread.currentThread().getName() + " handles " + request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
2.1.5. Main类¶
public class Main {
public static void main(String[] args) {
RequestQueue requestQueue = new RequestQueue();
new ServerThread(requestQueue, "Bobby", 6535897L).start();
new ClientThread(requestQueue, "Alice", 3141592L).start();
}
}
2.1.6. 运行(例子)¶
通过运行可知,无论服务线程和客户线程的开启先后,其第一个打印的线程必定为客户线程。
Alice requests [ Request No.0 ]
Bobby handles [ Request No.0 ]
Alice requests [ Request No.1 ]
Alice requests [ Request No.2 ]
Bobby handles [ Request No.1 ]
Bobby handles [ Request No.2 ]
Alice requests [ Request No.3 ]
Bobby handles [ Request No.3 ]
Alice requests [ Request No.4 ]
Bobby handles [ Request No.4 ]
Alice requests [ Request No.5 ]
Alice requests [ Request No.6 ]
Bobby handles [ Request No.5 ]
Bobby handles [ Request No.6 ]
Alice requests [ Request No.7 ]
Bobby handles [ Request No.7 ]
Alice requests [ Request No.8 ]
Bobby handles [ Request No.8 ]
小技巧
使用 Guarded suspension 模式需要注意程序的生存性。考虑到这种模式的实现通过 wait 以及 notifyAll 来实现,需要意识到会有执行了 wait 但是却没执行 notifyAll,以及执行了 notifyAll 但是从来没有执行 wait 的极端情况。
2.2. Balking mode 实例(不需要就算了)¶
类名 |
说明 |
|---|---|
Data |
可修改并保存的数据的类 |
SaverThread |
定期保存数据的类 |
ChangerThread |
修改并保存数据内容的类 |
Main |
测试类 |
2.2.1. Data¶
import java.io.IOException;
import java.io.FileWriter;
import java.io.Writer;
public class Data {
private final String filename; // 保存的文件名称
private String content; // 数据内容
private boolean changed; // 修改后的内容若未保存,则为true
public Data(String filename, String content) {
this.filename = filename;
this.content = content;
this.changed = true;
}
// 修改数据内容
public synchronized void change(String newContent) {
content = newContent;
changed = true;
}
// 若数据内容修改过,则保存到文件中
public synchronized void save() throws IOException {
if (!changed) {
return;
}
doSave();
changed = false;
}
// 将数据内容实际保存到文件中
private void doSave() throws IOException {
System.out.println(Thread.currentThread().getName() + " calls doSave, content = " + content);
Writer writer = new FileWriter(filename);
writer.write(content);
writer.close();
}
}
2.2.2. ChangerThread¶
import java.io.IOException;
import java.util.Random;
public class ChangerThread extends Thread {
private final Data data;
private final Random random = new Random();
public ChangerThread(String name, Data data) {
super(name);
this.data = data;
}
public void run() {
try {
for (int i = 0; true; i++) {
data.change("No." + i); // 修改数据
Thread.sleep(random.nextInt(1000)); // 执行其他操作
data.save(); // 显式地保存
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.2.3. SaverThread¶
import java.io.IOException;
public class SaverThread extends Thread {
private final Data data;
public SaverThread(String name, Data data) {
super(name);
this.data = data;
}
public void run() {
try {
while (true) {
data.save(); // 要求保存数据
Thread.sleep(1000); // 休眠约1秒
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.2.4. Main¶
public class Main {
public static void main(String[] args) {
Data data = new Data("data.txt", "(empty)");
new ChangerThread("ChangerThread", data).start();
new SaverThread("SaverThread", data).start();
}
}
2.2.5. 运行(实例)¶
SaverThread calls doSave, content = No.0
SaverThread calls doSave, content = No.1
ChangerThread calls doSave, content = No.2
SaverThread calls doSave, content = No.3
ChangerThread calls doSave, content = No.4
SaverThread calls doSave, content = No.5
SaverThread calls doSave, content = No.6
ChangerThread calls doSave, content = No.7
SaverThread calls doSave, content = No.8
ChangerThread calls doSave, content = No.9
ChangerThread calls doSave, content = No.10
ChangerThread calls doSave, content = No.11
SaverThread calls doSave, content = No.12
ChangerThread calls doSave, content = No.13
ChangerThread calls doSave, content = No.14
SaverThread calls doSave, content = No.15
小技巧
可使用 Balking 模式的情况————实现闭锁。所谓闭锁,一般针对“状态仅变化一次的变量”,如下Something类中的 initialized 变量一样,一般针对的是初始化以及终止处理这类“不会执行两次及以上的处理”。
public class Something {
private boolean initialized = false;
public synchronized void init() {
if (initialized) {
return;
}
doInit();
initialized = true;
}
private void doInit() {
// 实际的初始化处理
}
}