1 线程的异步性
线程的异步性是由于多个线程异步的并发修改同一数据引起的。以下代码创建了100个线程,每个线程异步的向account对象中增加1分钱,运行该程序会发现最终结果并非100,而且每次运行结果都不相同。通过该程序可使学生直观的观察到线程异步性的问题,教师
import java.util.concurrent.*;
public class AccountWithoutSync {
private static Account account = new Account();
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
// Create and launch 100 threads
for (int i = 0; i < 100; i++) {
executor.execute(new AddAPennyTask()); }
executor.shutdown();
// Wait until all tasks are finished
while (!executor.isTerminated()) { }
System.out.println("What is balance? " + account.getBalance());}
//A thread for adding a penny to the account
private static class AddAPennyTask implements Runnable {
public void run() {
account.deposit(1); } }
//An inner class for account
private static class Account {
private int balance = 0;
public int getBalance() {
return balance; }
public void deposit(int amount) {
int newBalance = balance + amount;
// This delay is deliberately added to magnify the
// data-corruption problem and make it easy to see.
try{
Thread.sleep(5); }
catch (InterruptedException ex) { }
balance = newBalance; } } }
可在此基础上提出竞争条件和临界区等基本概念,然后引入同步机制解决该代码的异步问题,如此学生可深刻意识到操作系统理论解决实际问题的重要性,产生对后续理论深入学习的兴趣。
2 信号量
信号量(Semaphore)由Edsger Dijkstra于60年代提出,其主要用于限制访问共享资源的线程数目[1],Java语言中的Semaphore类即是信号量概念的具体实现,其用法如下:
Semaphore sem = new Semaphore(n);
try{
sem.acquire(); //P操作
//critical section
}catch(InterruptedException ie){ }
finally{
sem.release(); //V操作
}
//remainder section
教师可在介绍其内部实现原理后,让学生用二元信号量(binarysemaphore)解决2.1中的代码异步问题,立即将理论应用于实践,以加深对理论的理解。
3 管程
管程(Monitor)是由Brinch Hansen和Tony Hoare于70年代提出的一种高级同步原语[2],寻常系统中已很少看到具体实现,以至许多学生甚至教师认为其仅是理想化的概念,所以通过伪语言很难向学生阐明其便利性和高效性。其实Java语言提供的synchronized同步锁即为管程的实现,Java语言的每个对象都有一个隐含锁(Lock)和一个内置条件变量(Condition),如图1所示,等待获取对象锁的线程都在该对象的进入队列(entry set)中,因条件变量阻塞的线程都在该条件所对应的队列(wait set)中。
图1 Java语言实现的管程结构图
管程的发明者Brinch Hansen于1999年曾撰文对Java语言的管程的实现提出了中肯的评价。以下代码利用synchronized同步锁的管程实现展示了线程间的协作关系。
//Thread1
synchronized(anObject) {
try{
//Wait for the condition to become true
while (!condition)
anObject.wait();
//Do something when condition is true
}catch (InterruptedException ex) {
ex.printStackTrace(); } }
//Thread2
synchronized(anObject) {
//When condition becomes true
anObject.notify(); //oranObject.notifyAll(); }