Java语言描述经典同步问题

来源:互联网 发布:校园广播系统 杰网络 编辑:程序博客网 时间:2024/05/05 16:25

1  生产者-消费者问题

生产者-消费者(producer-consumer)问题也就是有界缓冲区(bounded-buffer)问题,即生产者不断的往有界缓冲区中放产品,消费者不断从中取产品,在保证两者互斥的基础上,当缓冲区满时生产者要阻塞,等待消费者取产品后将其唤醒,当缓冲区空时消费者要阻塞,等待生产者放产品后将其唤醒。用Java信号量描述的算法如下:

//互斥信号量

Semaphoremutex =new Semaphore(1);

//空缓冲区数量

Semaphoreempty = new Semaphore(BUFFER_SIZE);

//满缓冲区数量

Semaphorefull = new Semaphore(0);

// producer calls this method

public void insert(Object item) {

  empty.acquire();

  mutex.acquire();

  //add an item to the buffer

 buffer[in] = item;

  in= (in + 1) % BUFFER_SIZE;

  mutex.release();

  full.release(); }

// consumer calls this method

public Object remove() {

  full.acquire();

  mutex.acquire();

  //remove an item from the buffer

 Object item = buffer[out];

  out= (out + 1) % BUFFER_SIZE;

  mutex.release();

  empty.release();

 return item; }

分别创建生产者线程和消费者线程调用相应方法,在程序运行正确后,可调换互斥信号量与资源信号量的位置,学生即可观察到死锁的发生。用Java的synchronized同步锁或ReentrantLock锁也可描述该算法,但传统低级别的API较难使用,易导致程序结构混乱和性能问题,因此Java语言提供了BlockingQueue接口,使得同步操作完全对用户透明。BlockingQueue接口的实现类有ArrayBlockingQueue、LinkedBlockingQueue和PriorityBlockingQueue三种,以ArrayBlockingQueue为例描述生产者-消费者问题的算法如下:

 

ArrayBlockingQueue<Integer>buffer =

newArrayBlockingQueue<Integer>(BUFFER_SIZE);

// A task for adding an int to the buffer

while (true) {

  try{

    buffer.put(i++); // Add any value tothe buffer

  }catch (InterruptedException ex) { } }

// A task for reading and deleting an intfrom the buffer

while (true) {

  try{

    buffer.take();

  }catch (InterruptedException ex) { } }

通过BlockingQueue接口的使用,使学生看到,随着技术的发展,并发程序设计将会变得更加简单而且逐步普及,从而建立起学习进程管理的信心和兴趣。

2  读者-写者问题

读者-写者(Reader-Writer)是保证一个Writer线程必须与其他线程互斥的访问共享对象的同步问题。用Java信号量描述的算法如下:

intreaderCount = 0;

Semaphoremutex = new Semaphore(1);

Semaphorewriter = new Semaphore(1);

//writer thread

writer.acquire();

 //writing is performed

writer.release();

//reader thread

mutex.acquire();

readerCount++;

if(readerCount == 1)

  writer.acquire();

mutex.release();

 //reading is performed

mutex.acquire();

readerCount--;

if(readerCount == 0)

  writer.release();

mutex.release();

同样可用Java的synchronized同步锁或ReentrantLock锁描述该算法,但Java语言提供的ReentrantReadWriteLock类使得读者-写者问题的编程更为简单,且其比synchronized同步锁具有更高的并发性能,算法如下:

ReentrantReadWriteLockrwl =

newReentrantReadWriteLock();

//Extract read and write locks

LockreadLock = rwl.readLock();

LockwriteLock = rwl.writeLock();

//Use the read lock in all accessors:

readLock.lock();

try { . . . }

finally { readLock.unlock(); }

//Use the write lock in all mutators:

writeLock.lock();

try { . . . }

finally { writeLock.unlock(); }

    修改ReentrantReadWriteLock类的构造方法,可分别实现写者优先和读者优先算法,简单的修改代码,学生便可直观的观测到两种策略的不同结果。

3  哲学家进餐问题

    由Dijkstra引入的哲学家进餐问题是需要在多个线程之间分配多个资源且不会出现死锁和饥饿形式的简单表示[3]。用Java信号量描述的算法如下:

Semaphore[]chopStick = new Semaphore[5];

for(int i=0; i<5; i++)

  chopStick[i] = new Semaphore(1);

while (true) {

 //get left chopstick

  chopStick[i].acquire();

 //get right chopstick

  chopStick[(i+1) % 5].acquire();

 eating();

 //return left chopstick

  chopStick[i].release();

  chopStick[(i+1) % 5].release();

 thinking();

}

    学生可运行并修改程序以观察死锁和饥饿问题,然后对其改进以解决死锁和饥饿问题。通过运行真实的代码,学生就有了真实的经验,传统的伪语言或动画模拟是无法达到该效果的。

0 0
原创粉丝点击